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
35using namespace std;
36
37map<string,string> g_buildVars;
38
39static unsigned int
40get_thread_count()
41{
42    unsigned int threads = std::thread::hardware_concurrency();
43    // Guess if the value cannot be computed
44    return threads == 0 ? 4 : static_cast<unsigned int>(threads * 1.3f);
45}
46
47string
48get_build_var(const string& buildTop, const string& name, bool quiet)
49{
50    int err;
51
52    map<string,string>::iterator it = g_buildVars.find(name);
53    if (it == g_buildVars.end()) {
54        Command cmd("make");
55        cmd.AddArg("--no-print-directory");
56        cmd.AddArg(string("-j") + std::to_string(get_thread_count()));
57        cmd.AddArg("-C");
58        cmd.AddArg(buildTop);
59        cmd.AddArg("-f");
60        cmd.AddArg("build/core/config.mk");
61        cmd.AddArg(string("dumpvar-") + name);
62        cmd.AddEnv("CALLED_FROM_SETUP", "true");
63        cmd.AddEnv("BUILD_SYSTEM", "build/core");
64
65        string output = trim(get_command_output(cmd, &err, quiet));
66        if (err == 0) {
67            g_buildVars[name] = output;
68            return output;
69        } else {
70            return string();
71        }
72    } else {
73        return it->second;
74    }
75}
76
77string
78sniff_device_name(const string& buildOut, const string& product)
79{
80    string match("ro.build.product=" + product);
81
82    string base(buildOut + "/target/product");
83    DIR* dir = opendir(base.c_str());
84    if (dir == NULL) {
85        return string();
86    }
87
88    dirent* entry;
89    while ((entry = readdir(dir)) != NULL) {
90        if (entry->d_name[0] == '.') {
91            continue;
92        }
93        if (entry->d_type == DT_DIR) {
94            string filename(base + "/" + entry->d_name + "/system/build.prop");
95            vector<string> lines;
96            split_lines(&lines, read_file(filename));
97            for (size_t i=0; i<lines.size(); i++) {
98                if (lines[i] == match) {
99                    return entry->d_name;
100                }
101            }
102        }
103    }
104
105    closedir(dir);
106    return string();
107}
108
109void
110json_error(const string& filename, const char* error, bool quiet)
111{
112    if (!quiet) {
113        print_error("Unable to parse module info file (%s): %s", error, filename.c_str());
114        print_error("Have you done a full build?");
115    }
116    exit(1);
117}
118
119static void
120get_values(const Json::Value& json, const string& name, vector<string>* result)
121{
122    Json::Value nullValue;
123
124    const Json::Value& value = json.get(name, nullValue);
125    if (!value.isArray()) {
126        return;
127    }
128
129    const int N = value.size();
130    for (int i=0; i<N; i++) {
131        const Json::Value& child = value[i];
132        if (child.isString()) {
133            result->push_back(child.asString());
134        }
135    }
136}
137
138void
139read_modules(const string& buildOut, const string& device, map<string,Module>* result, bool quiet)
140{
141    string filename(string(buildOut + "/target/product/") + device + "/module-info.json");
142    std::ifstream stream(filename, std::ifstream::binary);
143
144    if (stream.fail()) {
145        if (!quiet) {
146            print_error("Unable to open module info file: %s", filename.c_str());
147            print_error("Have you done a full build?");
148        }
149        exit(1);
150    }
151
152    Json::Value json;
153    Json::Reader reader;
154    if (!reader.parse(stream, json)) {
155        json_error(filename, "can't parse json format", quiet);
156        return;
157    }
158
159    if (!json.isObject()) {
160        json_error(filename, "root element not an object", quiet);
161        return;
162    }
163
164    vector<string> names = json.getMemberNames();
165    const int N = names.size();
166    for (int i=0; i<N; i++) {
167        const string& name = names[i];
168
169        const Json::Value& value = json[name];
170        if (!value.isObject()) {
171            continue;
172        }
173
174        Module module;
175
176        module.name = name;
177        get_values(value, "class", &module.classes);
178        get_values(value, "path", &module.paths);
179        get_values(value, "installed", &module.installed);
180
181        // Only keep classes we can handle
182        for (ssize_t i = module.classes.size() - 1; i >= 0; i--) {
183            string cl = module.classes[i];
184            if (!(cl == "JAVA_LIBRARIES" || cl == "EXECUTABLES" || cl == "SHARED_LIBRARIES"
185                    || cl == "APPS")) {
186                module.classes.erase(module.classes.begin() + i);
187            }
188        }
189        if (module.classes.size() == 0) {
190            continue;
191        }
192
193        // Only target modules (not host)
194        for (ssize_t i = module.installed.size() - 1; i >= 0; i--) {
195            string fn = module.installed[i];
196            if (!starts_with(fn, buildOut + "/target/")) {
197                module.installed.erase(module.installed.begin() + i);
198            }
199        }
200        if (module.installed.size() == 0) {
201            continue;
202        }
203
204        (*result)[name] = module;
205    }
206}
207
208int
209build_goals(const vector<string>& goals)
210{
211    Command cmd("make");
212    cmd.AddArg(string("-j") + std::to_string(get_thread_count()));
213    cmd.AddArg("-f");
214    cmd.AddArg("build/core/main.mk");
215    for (size_t i=0; i<goals.size(); i++) {
216        cmd.AddArg(goals[i]);
217    }
218
219    return run_command(cmd);
220}
221
222