1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "make.h"
18 
19 #include "command.h"
20 #include "print.h"
21 #include "util.h"
22 
23 #include <json/reader.h>
24 #include <json/value.h>
25 
26 #include <fstream>
27 #include <string>
28 #include <map>
29 #include <thread>
30 
31 #include <sys/types.h>
32 #include <dirent.h>
33 #include <string.h>
34 
35 using namespace std;
36 
37 map<string,string> g_buildVars;
38 
39 string
get_build_var(const string & name,bool quiet)40 get_build_var(const string& name, bool quiet)
41 {
42     int err;
43 
44     map<string,string>::iterator it = g_buildVars.find(name);
45     if (it == g_buildVars.end()) {
46         Command cmd("build/soong/soong_ui.bash");
47         cmd.AddArg("--dumpvar-mode");
48         cmd.AddArg(name);
49 
50         string output = trim(get_command_output(cmd, &err, quiet));
51         if (err == 0) {
52             g_buildVars[name] = output;
53             return output;
54         } else {
55             return string();
56         }
57     } else {
58         return it->second;
59     }
60 }
61 
62 string
sniff_device_name(const string & buildOut,const string & product)63 sniff_device_name(const string& buildOut, const string& product)
64 {
65     string match("ro.build.product=" + product);
66 
67     string base(buildOut + "/target/product");
68     DIR* dir = opendir(base.c_str());
69     if (dir == NULL) {
70         return string();
71     }
72 
73     dirent* entry;
74     while ((entry = readdir(dir)) != NULL) {
75         if (entry->d_name[0] == '.') {
76             continue;
77         }
78         if (entry->d_type == DT_DIR) {
79             string filename(base + "/" + entry->d_name + "/system/build.prop");
80             vector<string> lines;
81             split_lines(&lines, read_file(filename));
82             for (size_t i=0; i<lines.size(); i++) {
83                 if (lines[i] == match) {
84                     return entry->d_name;
85                 }
86             }
87         }
88     }
89 
90     closedir(dir);
91     return string();
92 }
93 
94 void
json_error(const string & filename,const char * error,bool quiet)95 json_error(const string& filename, const char* error, bool quiet)
96 {
97     if (!quiet) {
98         print_error("Unable to parse module info file (%s): %s", error, filename.c_str());
99         print_error("Have you done a full build?");
100     }
101     exit(1);
102 }
103 
104 static void
get_values(const Json::Value & json,const string & name,vector<string> * result)105 get_values(const Json::Value& json, const string& name, vector<string>* result)
106 {
107     Json::Value nullValue;
108 
109     const Json::Value& value = json.get(name, nullValue);
110     if (!value.isArray()) {
111         return;
112     }
113 
114     const int N = value.size();
115     for (int i=0; i<N; i++) {
116         const Json::Value& child = value[i];
117         if (child.isString()) {
118             result->push_back(child.asString());
119         }
120     }
121 }
122 
123 void
read_modules(const string & buildOut,const string & device,map<string,Module> * result,bool quiet)124 read_modules(const string& buildOut, const string& device, map<string,Module>* result, bool quiet)
125 {
126     string filename(string(buildOut + "/target/product/") + device + "/module-info.json");
127     std::ifstream stream(filename, std::ifstream::binary);
128 
129     if (stream.fail()) {
130         if (!quiet) {
131             print_error("Unable to open module info file: %s", filename.c_str());
132             print_error("Have you done a full build?");
133         }
134         exit(1);
135     }
136 
137     Json::Value json;
138     Json::Reader reader;
139     if (!reader.parse(stream, json)) {
140         json_error(filename, "can't parse json format", quiet);
141         return;
142     }
143 
144     if (!json.isObject()) {
145         json_error(filename, "root element not an object", quiet);
146         return;
147     }
148 
149     vector<string> names = json.getMemberNames();
150     const int N = names.size();
151     for (int i=0; i<N; i++) {
152         const string& name = names[i];
153 
154         const Json::Value& value = json[name];
155         if (!value.isObject()) {
156             continue;
157         }
158 
159         Module module;
160 
161         module.name = name;
162         get_values(value, "class", &module.classes);
163         get_values(value, "path", &module.paths);
164         get_values(value, "installed", &module.installed);
165 
166         // Only keep classes we can handle
167         for (ssize_t i = module.classes.size() - 1; i >= 0; i--) {
168             string cl = module.classes[i];
169             if (!(cl == "JAVA_LIBRARIES" || cl == "EXECUTABLES" || cl == "SHARED_LIBRARIES"
170                     || cl == "APPS" || cl == "NATIVE_TESTS")) {
171                 module.classes.erase(module.classes.begin() + i);
172             }
173         }
174         if (module.classes.size() == 0) {
175             continue;
176         }
177 
178         // Only target modules (not host)
179         for (ssize_t i = module.installed.size() - 1; i >= 0; i--) {
180             string fn = module.installed[i];
181             if (!starts_with(fn, buildOut + "/target/")) {
182                 module.installed.erase(module.installed.begin() + i);
183             }
184         }
185         if (module.installed.size() == 0) {
186             continue;
187         }
188 
189         (*result)[name] = module;
190     }
191 }
192 
193 int
build_goals(const vector<string> & goals)194 build_goals(const vector<string>& goals)
195 {
196     Command cmd("build/soong/soong_ui.bash");
197     cmd.AddArg("--make-mode");
198     for (size_t i=0; i<goals.size(); i++) {
199         cmd.AddArg(goals[i]);
200     }
201 
202     return run_command(cmd);
203 }
204 
205