You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
283 lines
7.2 KiB
283 lines
7.2 KiB
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "make.h"
|
|
|
|
#include "command.h"
|
|
#include "print.h"
|
|
#include "util.h"
|
|
|
|
#include <json/reader.h>
|
|
#include <json/writer.h>
|
|
#include <json/value.h>
|
|
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <map>
|
|
#include <thread>
|
|
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <string.h>
|
|
|
|
using namespace std;
|
|
|
|
static bool
|
|
map_contains(const map<string,string>& m, const string& k, const string& v) {
|
|
map<string,string>::const_iterator it = m.find(k);
|
|
if (it == m.end()) {
|
|
return false;
|
|
}
|
|
return it->second == v;
|
|
}
|
|
|
|
static string
|
|
make_cache_filename(const string& outDir)
|
|
{
|
|
string filename(outDir);
|
|
return filename + "/.bit_cache";
|
|
}
|
|
|
|
bool
|
|
Module::HasClass(const string& cl)
|
|
{
|
|
for (vector<string>::const_iterator c = classes.begin(); c != classes.end(); c++) {
|
|
if (*c == cl) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
BuildVars::BuildVars(const string& outDir, const string& buildProduct,
|
|
const string& buildVariant, const string& buildType)
|
|
:m_filename(),
|
|
m_cache()
|
|
{
|
|
m_cache["TARGET_PRODUCT"] = buildProduct;
|
|
m_cache["TARGET_BUILD_VARIANT"] = buildVariant;
|
|
m_cache["TARGET_BUILD_TYPE"] = buildType;
|
|
|
|
// If we have any problems reading the file, that's ok, just do
|
|
// uncached calls to make / soong.
|
|
|
|
if (outDir == "") {
|
|
return;
|
|
}
|
|
|
|
|
|
m_filename = make_cache_filename(outDir);
|
|
|
|
std::ifstream stream(m_filename, std::ifstream::binary);
|
|
|
|
if (stream.fail()) {
|
|
return;
|
|
}
|
|
|
|
Json::Value json;
|
|
Json::CharReaderBuilder builder;
|
|
if (!Json::parseFromStream(builder, stream, &json, /* errorMessage = */ nullptr)) {
|
|
return;
|
|
}
|
|
|
|
if (!json.isObject()) {
|
|
return;
|
|
}
|
|
|
|
map<string,string> cache;
|
|
|
|
vector<string> names = json.getMemberNames();
|
|
const int N = names.size();
|
|
for (int i=0; i<N; i++) {
|
|
const string& name = names[i];
|
|
const Json::Value& value = json[name];
|
|
if (!value.isString()) {
|
|
continue;
|
|
}
|
|
cache[name] = value.asString();
|
|
}
|
|
|
|
// If all of the base variables match, then we can use this cache. Otherwise, use our
|
|
// base one. The next time someone reads a value, the new one, with our base varaibles
|
|
// will be saved.
|
|
if (map_contains(cache, "TARGET_PRODUCT", buildProduct)
|
|
&& map_contains(cache, "TARGET_BUILD_VARIANT", buildVariant)
|
|
&& map_contains(cache, "TARGET_BUILD_TYPE", buildType)) {
|
|
m_cache = cache;
|
|
}
|
|
}
|
|
|
|
BuildVars::~BuildVars()
|
|
{
|
|
}
|
|
|
|
void
|
|
BuildVars::save()
|
|
{
|
|
if (m_filename == "") {
|
|
return;
|
|
}
|
|
|
|
Json::StreamWriterBuilder factory;
|
|
factory["indentation"] = " ";
|
|
std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter());
|
|
Json::Value json(Json::objectValue);
|
|
|
|
for (map<string,string>::const_iterator it = m_cache.begin(); it != m_cache.end(); it++) {
|
|
json[it->first] = it->second;
|
|
}
|
|
|
|
std::ofstream stream(m_filename, std::ofstream::binary);
|
|
writer->write(json, &stream);
|
|
}
|
|
|
|
string
|
|
BuildVars::GetBuildVar(const string& name, bool quiet)
|
|
{
|
|
int err;
|
|
|
|
map<string,string>::iterator it = m_cache.find(name);
|
|
if (it == m_cache.end()) {
|
|
Command cmd("build/soong/soong_ui.bash");
|
|
cmd.AddArg("--dumpvar-mode");
|
|
cmd.AddArg(name);
|
|
|
|
string output = trim(get_command_output(cmd, &err, quiet));
|
|
if (err == 0) {
|
|
m_cache[name] = output;
|
|
save();
|
|
return output;
|
|
} else {
|
|
return string();
|
|
}
|
|
} else {
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
void
|
|
json_error(const string& filename, const char* error, bool quiet)
|
|
{
|
|
if (!quiet) {
|
|
print_error("Unable to parse module info file (%s): %s", error, filename.c_str());
|
|
print_error("Have you done a full build?");
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
static void
|
|
get_values(const Json::Value& json, const string& name, vector<string>* result)
|
|
{
|
|
Json::Value nullValue;
|
|
|
|
const Json::Value& value = json.get(name, nullValue);
|
|
if (!value.isArray()) {
|
|
return;
|
|
}
|
|
|
|
const int N = value.size();
|
|
for (int i=0; i<N; i++) {
|
|
const Json::Value& child = value[i];
|
|
if (child.isString()) {
|
|
result->push_back(child.asString());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
read_modules(const string& buildOut, const string& device, map<string,Module>* result, bool quiet)
|
|
{
|
|
string filename(string(buildOut + "/target/product/") + device + "/module-info.json");
|
|
std::ifstream stream(filename, std::ifstream::binary);
|
|
|
|
if (stream.fail()) {
|
|
if (!quiet) {
|
|
print_error("Unable to open module info file: %s", filename.c_str());
|
|
print_error("Have you done a full build?");
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
Json::Value json;
|
|
Json::CharReaderBuilder builder;
|
|
if (!Json::parseFromStream(builder, stream, &json, /* errorMessage = */ nullptr)) {
|
|
json_error(filename, "can't parse json format", quiet);
|
|
return;
|
|
}
|
|
|
|
if (!json.isObject()) {
|
|
json_error(filename, "root element not an object", quiet);
|
|
return;
|
|
}
|
|
|
|
vector<string> names = json.getMemberNames();
|
|
const int N = names.size();
|
|
for (int i=0; i<N; i++) {
|
|
const string& name = names[i];
|
|
|
|
const Json::Value& value = json[name];
|
|
if (!value.isObject()) {
|
|
continue;
|
|
}
|
|
|
|
Module module;
|
|
|
|
module.name = name;
|
|
get_values(value, "class", &module.classes);
|
|
get_values(value, "path", &module.paths);
|
|
get_values(value, "installed", &module.installed);
|
|
|
|
// Only keep classes we can handle
|
|
for (ssize_t i = module.classes.size() - 1; i >= 0; i--) {
|
|
string cl = module.classes[i];
|
|
if (!(cl == "JAVA_LIBRARIES" || cl == "EXECUTABLES" || cl == "SHARED_LIBRARIES"
|
|
|| cl == "APPS" || cl == "NATIVE_TESTS")) {
|
|
module.classes.erase(module.classes.begin() + i);
|
|
}
|
|
}
|
|
if (module.classes.size() == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Only target modules (not host)
|
|
for (ssize_t i = module.installed.size() - 1; i >= 0; i--) {
|
|
string fn = module.installed[i];
|
|
if (!starts_with(fn, buildOut + "/target/")) {
|
|
module.installed.erase(module.installed.begin() + i);
|
|
}
|
|
}
|
|
if (module.installed.size() == 0) {
|
|
continue;
|
|
}
|
|
|
|
(*result)[name] = module;
|
|
}
|
|
}
|
|
|
|
int
|
|
build_goals(const vector<string>& goals)
|
|
{
|
|
Command cmd("build/soong/soong_ui.bash");
|
|
cmd.AddArg("--make-mode");
|
|
for (size_t i=0; i<goals.size(); i++) {
|
|
cmd.AddArg(goals[i]);
|
|
}
|
|
|
|
return run_command(cmd);
|
|
}
|
|
|