Make fetch_cvd flags more concise.
There are now three flags to control the build:
-default_build sets the baseline image, including vendor and host tools
-system_build sets the system.img and product.img source
-kernel_build sets the kernel/gki source
These three flags all accept builds in the format
-branch_name/target_name
-build_id/target_name
-branch_name
-build_id
Giving a branch name will use the latest build id from the branch.
If missing the target_name will default to aosp_cf_x86_phone-userdebug.
This also implements downloading alternate kernel builds.
Test: Run with a kernel from a different branch
Bug: 137304531
Change-Id: Ic3715d64b7ea51ca6ca1f83023f415a56faa07b1
diff --git a/host/commands/fetcher/build_api.cc b/host/commands/fetcher/build_api.cc
index 7823cab..a82a7c6 100644
--- a/host/commands/fetcher/build_api.cc
+++ b/host/commands/fetcher/build_api.cc
@@ -54,17 +54,15 @@
+ "&buildType=submitted&maxResults=1&successful=true&target=" + target;
auto response = curl.DownloadToJson(url, Headers());
if (response["builds"].size() != 1) {
- LOG(ERROR) << "invalid number of builds\n";
+ LOG(WARNING) << "invalid number of builds\n";
return "";
}
return response["builds"][0]["buildId"].asString();
}
-std::vector<Artifact> BuildApi::Artifacts(const std::string& build_id,
- const std::string& target,
- const std::string& attempt_id) {
- std::string url = BUILD_API + "/builds/" + build_id + "/" + target
- + "/attempts/" + attempt_id + "/artifacts?maxResults=1000";
+std::vector<Artifact> BuildApi::Artifacts(const DeviceBuild& build) {
+ std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target
+ + "/attempts/latest/artifacts?maxResults=1000";
auto artifacts_json = curl.DownloadToJson(url, Headers());
std::vector<Artifact> artifacts;
for (const auto& artifact_json : artifacts_json["artifacts"]) {
@@ -73,12 +71,38 @@
return artifacts;
}
-bool BuildApi::ArtifactToFile(const std::string& build_id,
- const std::string& target,
- const std::string& attempt_id,
+bool BuildApi::ArtifactToFile(const DeviceBuild& build,
const std::string& artifact,
const std::string& path) {
- std::string url = BUILD_API + "/builds/" + build_id + "/" + target
- + "/attempts/" + attempt_id + "/artifacts/" + artifact + "?alt=media";
+ std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target
+ + "/attempts/latest/artifacts/" + artifact + "?alt=media";
return curl.DownloadToFile(url, path, Headers());
}
+
+DeviceBuild ArgumentToBuild(BuildApi* build_api, const std::string& arg) {
+ size_t slash_pos = arg.find('/');
+ if (slash_pos != std::string::npos
+ && arg.find('/', slash_pos + 1) != std::string::npos) {
+ LOG(FATAL) << "Build argument cannot have more than one '/' slash. Was at "
+ << slash_pos << " and " << arg.find('/', slash_pos + 1);
+ }
+ std::string build_target = slash_pos == std::string::npos
+ ? "aosp_cf_x86_phone-userdebug" : arg.substr(slash_pos + 1);
+ std::string branch_or_id = slash_pos == std::string::npos
+ ? arg: arg.substr(0, slash_pos);
+ std::string branch_latest_build_id =
+ build_api->LatestBuildId(branch_or_id, build_target);
+ if (branch_latest_build_id != "") {
+ LOG(INFO) << "The latest good build on branch \"" << branch_or_id
+ << "\"with build target \"" << build_target
+ << "is \"" << branch_latest_build_id << "\"";
+ return DeviceBuild(branch_latest_build_id, build_target);
+ } else {
+ DeviceBuild proposed_build = DeviceBuild(branch_or_id, build_target);
+ if (build_api->Artifacts(proposed_build).size() == 0) {
+ LOG(FATAL) << '"' << branch_or_id << "\" with build target \""
+ << build_target << "\" is not a valid branch or build id.";
+ }
+ return proposed_build;
+ }
+}
diff --git a/host/commands/fetcher/build_api.h b/host/commands/fetcher/build_api.h
index be5596a..128c525 100644
--- a/host/commands/fetcher/build_api.h
+++ b/host/commands/fetcher/build_api.h
@@ -44,6 +44,16 @@
unsigned int Crc32() const { return crc32; }
};
+struct DeviceBuild {
+ DeviceBuild(const std::string& id, const std::string& target) {
+ this->id = id;
+ this->target = target;
+ }
+
+ std::string id;
+ std::string target;
+};
+
class BuildApi {
CurlWrapper curl;
std::unique_ptr<CredentialSource> credential_source;
@@ -56,11 +66,10 @@
std::string LatestBuildId(const std::string& branch,
const std::string& target);
- std::vector<Artifact> Artifacts(const std::string& build_id,
- const std::string& target,
- const std::string& attempt_id);
+ std::vector<Artifact> Artifacts(const DeviceBuild&);
- bool ArtifactToFile(const std::string& build_id, const std::string& target,
- const std::string& attempt_id,
- const std::string& artifact, const std::string& path);
+ bool ArtifactToFile(const DeviceBuild& build, const std::string& artifact,
+ const std::string& path);
};
+
+DeviceBuild ArgumentToBuild(BuildApi* api, const std::string& arg);
diff --git a/host/commands/fetcher/main.cc b/host/commands/fetcher/main.cc
index d5c1cac..4f3e35d 100644
--- a/host/commands/fetcher/main.cc
+++ b/host/commands/fetcher/main.cc
@@ -28,10 +28,11 @@
#include "credential_source.h"
#include "install_zip.h"
-// TODO(schuffelen): Mixed builds.
-DEFINE_string(build_id, "latest", "Build ID for all artifacts");
-DEFINE_string(branch, "aosp-master", "Branch when build_id=\"latest\"");
-DEFINE_string(target, "aosp_cf_x86_phone-userdebug", "Build target");
+DEFINE_string(default_build, "aosp-master/aosp_cf_x86_phone-userdebug",
+ "source for the cuttlefish build to use (vendor.img + host)");
+DEFINE_string(system_build, "", "source for system.img and product.img");
+DEFINE_string(kernel_build, "", "source for the kernel or gki target");
+
DEFINE_string(credential_source, "", "Build API credential source");
DEFINE_string(system_image_build_target, "", "Alternate target for the system "
"image");
@@ -45,34 +46,33 @@
const std::string& HOST_TOOLS = "cvd-host_package.tar.gz";
-std::string target_image_zip(std::string target, const std::string& build_id) {
+std::string target_image_zip(const DeviceBuild& build) {
+ std::string target = build.target;
if (target.find("-userdebug") != std::string::npos) {
target.replace(target.find("-userdebug"), sizeof("-userdebug"), "");
}
if (target.find("-eng") != std::string::npos) {
target.replace(target.find("-eng"), sizeof("-eng"), "");
}
- return target + "-img-" + build_id + ".zip";
+ return target + "-img-" + build.id + ".zip";
}
-bool download_images(BuildApi* build_api, const std::string& target,
- const std::string& build_id,
+bool download_images(BuildApi* build_api, const DeviceBuild& build,
const std::string& target_directory,
const std::vector<std::string>& images) {
- std::string img_zip_name = target_image_zip(target, build_id);
- auto artifacts = build_api->Artifacts(build_id, target, "latest");
+ std::string img_zip_name = target_image_zip(build);
+ auto artifacts = build_api->Artifacts(build);
bool has_image_zip = false;
for (const auto& artifact : artifacts) {
has_image_zip |= artifact.Name() == img_zip_name;
}
if (!has_image_zip) {
- LOG(ERROR) << "Target " << target << " at id " << build_id
+ LOG(ERROR) << "Target " << build.target << " at id " << build.id
<< " did not have " << img_zip_name;
return false;
}
std::string local_path = target_directory + "/" + img_zip_name;
- build_api->ArtifactToFile(build_id, target, "latest",
- img_zip_name, local_path);
+ build_api->ArtifactToFile(build, img_zip_name, local_path);
auto could_extract = ExtractImages(local_path, target_directory, images);
if (!could_extract) {
@@ -84,27 +84,25 @@
}
return true;
}
-bool download_images(BuildApi* build_api, const std::string& target,
- const std::string& build_id,
+bool download_images(BuildApi* build_api, const DeviceBuild& build,
const std::string& target_directory) {
- return download_images(build_api, target, build_id, target_directory, {});
+ return download_images(build_api, build, target_directory, {});
}
-bool download_host_package(BuildApi* build_api, const std::string& target,
- const std::string& build_id,
+bool download_host_package(BuildApi* build_api, const DeviceBuild& build,
const std::string& target_directory) {
- auto artifacts = build_api->Artifacts(build_id, target, "latest");
+ auto artifacts = build_api->Artifacts(build);
bool has_host_package = false;
for (const auto& artifact : artifacts) {
has_host_package |= artifact.Name() == HOST_TOOLS;
}
if (!has_host_package) {
- LOG(ERROR) << "Target " << target << " at id " << build_id
+ LOG(ERROR) << "Target " << build.target << " at id " << build.id
<< " did not have " << HOST_TOOLS;
return false;
}
std::string local_path = target_directory + "/" + HOST_TOOLS;
- build_api->ArtifactToFile(build_id, target, "latest", HOST_TOOLS, local_path);
+ build_api->ArtifactToFile(build, HOST_TOOLS, local_path);
if (cvd::execute({"/bin/tar", "xvf", local_path, "-C", target_directory}) != 0) {
LOG(FATAL) << "Could not extract " << local_path;
return false;
@@ -124,10 +122,20 @@
return true;
}
+std::string USAGE_MESSAGE =
+ "<flags>\n"
+ "\n"
+ "\"*_build\" flags accept values in the following format:\n"
+ "\"branch/build_target\" - latest build of \"branch\" for \"build_target\"\n"
+ "\"build_id/build_target\" - build \"build_id\" for \"build_target\"\n"
+ "\"branch\" - latest build of \"branch\" for \"aosp_cf_x86_phone-userdebug\"\n"
+ "\"build_id\" - build \"build_id\" for \"aosp_cf_x86_phone-userdebug\"\n";
+
} // namespace
int main(int argc, char** argv) {
::android::base::InitLogging(argv, android::base::StderrLogger);
+ gflags::SetUsageMessage(USAGE_MESSAGE);
gflags::ParseCommandLineFlags(&argc, &argv, true);
std::string target_dir = cvd::AbsolutePath(FLAGS_directory);
@@ -144,31 +152,35 @@
credential_source = FixedCredentialSource::make(FLAGS_credential_source);
}
BuildApi build_api(std::move(credential_source));
- std::string build_id = FLAGS_build_id;
- if (build_id == "latest") {
- build_id = build_api.LatestBuildId(FLAGS_branch, FLAGS_target);
+
+ DeviceBuild default_build = ArgumentToBuild(&build_api, FLAGS_default_build);
+
+ if (!download_host_package(&build_api, default_build, target_dir)) {
+ LOG(FATAL) << "Could not download host package with target "
+ << default_build.target << " and build id " << default_build.id;
+ }
+ if (!download_images(&build_api, default_build, target_dir)) {
+ LOG(FATAL) << "Could not download images with target "
+ << default_build.target << " and build id " << default_build.id;
}
- if (!download_host_package(&build_api, FLAGS_target, build_id,
- target_dir)) {
- LOG(FATAL) << "Could not download host package with target "
- << FLAGS_target << " and build id " << build_id;
- }
- if (!download_images(&build_api, FLAGS_target, build_id, target_dir)) {
- LOG(FATAL) << "Could not download images with target "
- << FLAGS_target << " and build id " << build_id;
- }
desparse(target_dir + "/userdata.img");
- if (FLAGS_system_image_build_id != "") {
- std::string system_target = FLAGS_system_image_build_target == ""
- ? FLAGS_target
- : FLAGS_system_image_build_target;
- if (!download_images(&build_api, system_target, FLAGS_system_image_build_id,
- target_dir, {"system.img"})) {
+
+ if (FLAGS_system_build != "") {
+ DeviceBuild system_build = ArgumentToBuild(&build_api, FLAGS_system_build);
+
+ if (!download_images(&build_api, system_build, target_dir,
+ {"system.img"})) {
LOG(FATAL) << "Could not download system image at target "
- << FLAGS_target << " and build id " << FLAGS_system_image_build_id;
+ << system_build.target << " and build id " << system_build.id;
}
}
+
+ if (FLAGS_kernel_build != "") {
+ DeviceBuild kernel_build = ArgumentToBuild(&build_api, FLAGS_kernel_build);
+
+ build_api.ArtifactToFile(kernel_build, "bzImage", target_dir + "/kernel");
+ }
}
curl_global_cleanup();