| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086 |
- Usm.Manifest manifest = null;
- string? build_path = null;
- bool replace_provides = false;
- bool debug_mode = false;
- Usm.ResourceFinder resfinder = null;
- Usm.ResourceFinder destresfinder = null;
- public static int manifest_main(string[] args) {
- paths = new Usm.Paths();
- var resfinder_paths = paths.clone();
- resfinder_paths.destination = "/";
- resfinder = new Usm.ResourceFinder(resfinder_paths);
- destresfinder = new Usm.ResourceFinder(paths);
- if(args.length < 2) {
- manifest_usage();
- return 255;
- }
- var verb = args[1];
-
- // Check for --replace and --debug flags for autoprovides
- if(verb == "autoprovides") {
- for(int i = 2; i < args.length; i++) {
- if(args[i] == "--replace") {
- replace_provides = true;
- } else if(args[i] == "--debug") {
- debug_mode = true;
- } else if(build_path == null) {
- build_path = args[i];
- }
- }
- } else if(verb == "build") {
- // Optional for data packages (a no-op); enforced for regular packages in build()
- if(args.length >= 3) {
- build_path = args[2];
- }
- } else if(verb != "remove" && verb != "acquire" && verb != "install" && verb != "package" && verb != "deploy" && verb != "test" && verb != "validate") {
- if(args.length < 3) {
- manifest_usage();
- return 255;
- }
- build_path = args[2];
- }
- if(!File.new_for_path("MANIFEST.usm").query_exists()) {
- printerr("No MANIFEST.usm file found in current directory.\n");
- return 254;
- }
- try {
- var element = new InvercargillJson.JsonElement.from_file("MANIFEST.usm");
- manifest = Usm.Manifest.get_mapper().materialise(element.as<Invercargill.Properties>());
- manifest.validate();
- }
- catch (Error e) {
- printerr(@"Could not read MANIFEST.usm: $(e.message)\n");
- return 253;
- }
- // Data packages define no build step: nothing is staged, no build directory
- // or build.tar.xz is ever created for them
- if(manifest.is_data_package && verb == "build") {
- printerr(@"Data package \"$(manifest.name)\" has no build step, nothing to build.\n");
- return 0;
- }
- // Automatically create a temporary directory for these commands if one was not provided.
- if(!manifest.is_data_package && (verb == "install" || verb == "autoprovides" || verb == "test" || verb == "validate")) {
- if(build_path == null) {
- var dir = File.new_build_filename("/tmp", Uuid.string_random());
- try {
- dir.make_directory();
- }
- catch(Error e) {
- printerr(@"Could not create temporary build directory, try specifying one instead: $(e.message)\n");
- return 255;
- }
- build_path = dir.get_path();
- }
- }
- // Automatically create the specified directory for these commands if it doesn't exist
- if(!manifest.is_data_package && build_path != null && (verb == "install" || verb == "autoprovides" || verb == "test" || verb == "build" || verb == "validate")) {
- var build_dir = File.new_for_path(build_path);
- if(!build_dir.query_exists()) {
- printerr(@"Creating build directory: $build_path\n");
- try {
- build_dir.make_directory_with_parents();
- }
- catch(Error e) {
- printerr(@"Could not create build directory: $(e.message)\n");
- return 255;
- }
- }
- }
- if(verb == "build") {
- return build();
- }
- if(verb == "install") {
- return install();
- }
- if(verb == "acquire") {
- return acquire();
- }
- if(verb == "remove") {
- return uininstall();
- }
- if(verb == "package") {
- return package();
- }
- if(verb == "deploy") {
- return manifest_deploy(args);
- }
- if(verb == "autoprovides") {
- return autoprovides();
- }
- if(verb == "test") {
- return test();
- }
- if(verb == "validate") {
- return validate();
- }
- manifest_usage();
- return 255;
- }
- private void manifest_usage() {
- printerr("USAGE:\n\tusm manifest build <build path>\n\tusm manifest install <build path>\n\tusm manifest remove\nusm manifest acquire\nusm manifest package\nusm manifest deploy [--exec CMD] [--base IMAGE] [--repository FILE]... [--no-build] [--installer-url URL]\nusm manifest autoprovides [--replace] [--debug] [build path]\nusm manifest test [build path]\nusm manifest validate [build path]\n");
- }
- /**
- * Checks one dependency phase of the loaded manifest against the resources
- * {@link finder} can see, printing the same "Missing … dependency" lines as
- * before for flat phases.
- *
- * Grouped phases choose the first group whose members are all present (this
- * flow installs nothing, so every group costs nothing and manifest order
- * wins); when no group is viable an itemised per-group report is printed.
- * Returns false when the phase's requirements are unmet.
- */
- private bool phase_dependencies_satisfied(Usm.DependencyPhase? phase, string label, Usm.ResourceFinder finder) {
- if(phase == null) {
- return true;
- }
- if(!phase.is_grouped) {
- var sane = true;
- foreach(var resource in phase.ordered_required()) {
- if(!finder.has_resource(resource)) {
- printerr(@"Missing $label dependency \"$resource\".\n");
- sane = false;
- }
- }
- return sane;
- }
- var groups = phase.ordered_groups();
- foreach(var group in groups) {
- var missing = group.where(r => !finder.has_resource(r)).to_vector();
- if(missing.length == 0) {
- return true;
- }
- }
- printerr(@"No viable $label dependency group:\n");
- for(uint index = 0; index < groups.length; index++) {
- printerr(@" Group $(index + 1) ($(groups[index].to_string(r => r.to_string(), ", "))):\n");
- foreach(var resource in groups[index].where(r => !finder.has_resource(r))) {
- printerr(@" Missing $label dependency \"$resource\".\n");
- }
- }
- return false;
- }
- private int build() {
- if(build_path == null) {
- manifest_usage();
- return 255;
- }
- // Create build directory if it doesn't exist
- var sane = phase_dependencies_satisfied(manifest.dependencies.manage, "management", resfinder) &
- phase_dependencies_satisfied(manifest.dependencies.build, "build", resfinder);
- if(!sane) {
- printerr("Could not build manifest, missing dependencies\n");
- return 252;
- }
- try {
- var proc = manifest.run_build(build_path, paths, SubprocessFlags.INHERIT_FDS);
- proc.wait_check();
- }
- catch(Error e) {
- printerr(@"Error running build exec: $(e.message)\n");
- return 251;
- }
- return 0;
- }
- private int install() {
- // Data packages define no build or install execs; their provides are
- // copied directly from the source tree
- if(manifest.is_data_package) {
- return install_data_package();
- }
- // Create build directory if it doesn't exist
- var build_dir = File.new_for_path(build_path);
- if(!build_dir.query_exists()) {
- printerr(@"Creating build directory: $build_path\n");
- try {
- build_dir.make_directory_with_parents();
- }
- catch(Error e) {
- printerr(@"Could not create build directory: $(e.message)\n");
- return 255;
- }
- }
- var sane = phase_dependencies_satisfied(manifest.dependencies.manage, "management", resfinder) &
- phase_dependencies_satisfied(manifest.dependencies.build, "build", resfinder) &
- phase_dependencies_satisfied(manifest.dependencies.runtime, "runtime", destresfinder);
- if(!sane) {
- printerr("Could not install manifest, missing dependencies\n");
- return 252;
- }
- try {
- var proc = manifest.run_build(build_path, paths, SubprocessFlags.INHERIT_FDS, frac => printerr(@"Building '$(manifest.name)': $((int)(frac*100))%\r"));
- proc.wait_check();
- printerr("\n");
- }
- catch(Error e) {
- printerr(@"\nError running build exec: $(e.message)\n");
- return 251;
- }
- var install_dir = File.new_build_filename("/tmp", Uuid.string_random());
- if(manifest.executables.install != null) {
- try {
- install_dir.make_directory();
- }
- catch(Error e) {
- printerr(@"Could not create temporary install directory: $(e.message)\n");
- return 250;
- }
- try {
- manifest.run_install(build_path, install_dir.get_path(), paths, Usm.InstallType.FRESH, SubprocessFlags.INHERIT_FDS).wait_check();
- }
- catch(Error e) {
- printerr(@"Error running install exec: $(e.message)\n");
- return 249;
- }
- }
- else {
- install_dir = null;
- }
- try {
- manifest.install_resources(Environment.get_current_dir(), build_path, install_dir?.get_path(), paths, (r, c, t, f) => {
- if(f == 0.0f) {
- printerr(@"Installing resource $(c+1)/$t: \"$(r)\"\n");
- }
- });
- }
- catch(Error e) {
- printerr(@"Error installing manifest resources: $(e.message)\n");
- return 248;
- }
- try {
- var proc = manifest.run_post_install(build_path, Usm.InstallType.FRESH, SubprocessFlags.INHERIT_FDS);
- if(proc != null) {
- proc.wait_check();
- }
- }
- catch(Error e) {
- printerr(@"Error running post install exec: $(e.message)\n");
- return 247;
- }
- return 0;
- }
- /**
- * Installs a data package: every provide is copied directly from the source
- * tree to its suggested path, with no build or install staging involved.
- */
- private int install_data_package() {
- if(!phase_dependencies_satisfied(manifest.dependencies.runtime, "runtime", destresfinder)) {
- printerr("Could not install manifest, missing dependencies\n");
- return 252;
- }
- try {
- manifest.install_resources(Environment.get_current_dir(), Environment.get_current_dir(), null, paths, (r, c, t, f) => {
- if(f == 0.0f) {
- printerr(@"Installing resource $(c+1)/$t: \"$(r)\"\n");
- }
- });
- }
- catch(Error e) {
- printerr(@"Error installing manifest resources: $(e.message)\n");
- return 248;
- }
- return 0;
- }
- private int acquire() {
- if(manifest.executables.acquire == null) {
- printerr(@"Manifest does not reference an acquire script\n");
- return 248;
- }
- // The acquire phase's own refs gate acquisition (it runs on the host)
- if(!phase_dependencies_satisfied(manifest.dependencies.acquire, "acquire", resfinder)) {
- printerr("Could not acquire manifest, missing dependencies\n");
- return 247;
- }
- try {
- var proc = manifest.run_acquire(SubprocessFlags.INHERIT_FDS);
- proc.wait_check();
- }
- catch(Error e) {
- printerr(@"Error running build exec: $(e.message)\n");
- return 251;
- }
- return 0;
- }
- private int uininstall() {
- if(!phase_dependencies_satisfied(manifest.dependencies.manage, "management", resfinder)) {
- printerr("Could not remove manifest, missing dependencies\n");
- return 252;
- }
- try {
- var proc = manifest.run_remove(Usm.RemoveType.FINAL, SubprocessFlags.INHERIT_FDS);
- if(proc != null) {
- proc.wait_check();
- }
- }
- catch(Error e) {
- printerr(@"Error running remove exec: $(e.message)\n");
- return 249;
- }
- // Do files and symlinks first
- foreach (var resource in manifest.provides.where(r => r.value.file_type != Usm.ManifestFileType.DIRECTORY)) {
- var path = paths.get_suggested_path_for_resource(resource.key);
- printerr(@"Deleting resource $(resource.key) from $path...");
- try {
- var file = File.new_for_path(path);
- if(file.query_exists()) {
- file.delete();
- printerr(" done\n");
- }
- else {
- printerr(" file missing\n");
- }
- }
- catch(Error e) {
- printerr(@" $(e.message)\n");
- return 250;
- }
- }
- // Do directories last
- foreach (var resource in manifest.provides.where(r => r.value.file_type == Usm.ManifestFileType.DIRECTORY)) {
- var path = paths.get_suggested_path_for_resource(resource.key);
- printerr(@"Deleting resource $(resource.key) from $path...");
- try {
- var file = File.new_for_path(path);
- if(file.query_exists()) {
- file.delete();
- printerr(" done\n");
- }
- else {
- printerr(" file missing\n");
- }
- }
- catch(IOError.NOT_EMPTY e) {
- printerr(" directory not empty\n");
- }
- catch(Error e) {
- printerr(@" $(e.message)\n");
- return 250;
- }
- }
- return 0;
- }
- private int package() {
- printerr("Writing archive...\n");
- var output = @"../$(manifest.name)-$(manifest.version).usmc";
- try {
- // Collect everything not excluded by the root .usmignore (falling
- // back to the .git-only default), pruning ignored directories
- var ignore = new Usm.UsmIgnore.from_root(Environment.get_current_dir());
- var entries = new Invercargill.DataStructures.Vector<string>();
- package_collect(".", ignore, entries);
- var list_file = File.new_build_filename("/tmp", Uuid.string_random());
- try {
- var stream = new DataOutputStream(list_file.replace(null, false, FileCreateFlags.REPLACE_DESTINATION));
- foreach(var entry in entries) {
- stream.put_string(@"./$entry\n");
- }
- stream.close();
- var proc = new Subprocess.newv(new string[] { "tar", "-cJf", output, "--no-recursion", "-T", list_file.get_path() }, SubprocessFlags.INHERIT_FDS);
- proc.wait_check();
- }
- finally {
- try {
- list_file.delete();
- }
- catch(Error e) {
- // The temporary file list is disposable; leaving it behind is harmless
- }
- }
- printerr(@"Wrote package to path \"$(output)\"\n");
- return 0;
- }
- catch(Error e) {
- printerr("Failed to write package\n");
- return 172;
- }
- }
- /**
- * Walks the package tree rooted at {@link relative_dir}, collecting the
- * relative paths of every file and directory not ignored by {@link ignore}.
- * Ignored directories are pruned: nothing beneath them is visited.
- */
- private static void package_collect(string relative_dir, Usm.UsmIgnore ignore, Invercargill.DataStructures.Vector<string> entries) throws Error {
- var folder = File.new_for_path(relative_dir);
- var enumerator = folder.enumerate_children("*", FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
- while(true) {
- FileInfo? info = enumerator.next_file();
- if(info == null) {
- break;
- }
- var relative_path = relative_dir == "." ? info.get_name() : @"$relative_dir/$(info.get_name())";
- var is_directory = info.get_file_type() == FileType.DIRECTORY;
- if(ignore.matches(relative_path, is_directory)) {
- continue;
- }
- entries.add(relative_path);
- if(is_directory) {
- package_collect(relative_path, ignore, entries);
- }
- }
- }
- private int autoprovides() {
- // Create build directory if it doesn't exist
- var build_dir = File.new_for_path(build_path);
- if(!build_dir.query_exists()) {
- printerr(@"Creating build directory: $build_path\n");
- try {
- build_dir.make_directory_with_parents();
- }
- catch(Error e) {
- printerr(@"Could not create build directory: $(e.message)\n");
- return 255;
- }
- }
- if(manifest.executables.install == null) {
- printerr(@"Autoprovides command only works with manifests that specify an installation executable.\n");
- return 1;
- }
- var sane = phase_dependencies_satisfied(manifest.dependencies.manage, "management", resfinder) &
- phase_dependencies_satisfied(manifest.dependencies.build, "build", resfinder) &
- phase_dependencies_satisfied(manifest.dependencies.runtime, "runtime", destresfinder);
- if(!sane) {
- printerr("Could not install manifest, missing dependencies\n");
- return 252;
- }
- try {
- Usm.ProgressDelegate? progress_callback = null;
- SubprocessFlags build_flags = SubprocessFlags.STDOUT_SILENCE;
-
- if (!debug_mode) {
- progress_callback = frac => printerr(@"Building '$(manifest.name)': $((int)(frac*100))%\r");
- } else {
- build_flags = SubprocessFlags.INHERIT_FDS;
- }
-
- var proc = manifest.run_build(build_path, paths, build_flags, progress_callback);
- proc.wait_check();
-
- if (!debug_mode) {
- printerr("\n");
- }
- }
- catch(Error e) {
- printerr(@"\nError running build exec: $(e.message)\n");
- return 251;
- }
- var install_dir = File.new_build_filename("/tmp", Uuid.string_random());
- try {
- install_dir.make_directory();
- }
- catch(Error e) {
- printerr(@"Could not create temporary install directory: $(e.message)\n");
- return 250;
- }
- try {
- SubprocessFlags install_flags = debug_mode ? SubprocessFlags.INHERIT_FDS : SubprocessFlags.STDOUT_SILENCE;
- manifest.run_install(build_path, install_dir.get_path(), paths, Usm.InstallType.FRESH, install_flags).wait_check();
- }
- catch(Error e) {
- printerr(@"Error running install exec: $(e.message)\n"
- );
- return 249;
- }
- try {
- var provides = autoprovides_scan_tree(install_dir.get_path(), install_dir.get_path());
- // Create a JSON representation of the provides dictionary
- var json_object = new InvercargillJson.JsonObject();
- foreach (var entry in provides) {
- // Handle special case: when pathBase is as-expected, path is empty, and type is reg
- // output string "as-expected" instead of full JSON object
- if(entry.value.path_base == Usm.ManifestFilePathBase.AS_EXPECTED &&
- entry.value.path == "" &&
- entry.value.file_type == Usm.ManifestFileType.REGULAR) {
- json_object.set_native(entry.key.to_string(), "as-expected");
- } else {
- var mapper = Usm.ManifestFile.get_mapper();
- json_object.set_native(entry.key.to_string(), mapper.map_from(entry.value));
- }
- }
-
- var json_string = json_object.as_element().stringify_pretty();
- if(replace_provides) {
- try {
- // Update the manifest's provides property
- manifest.provides = provides;
-
- // Serialize the updated manifest back to JSON
- var mapper = Usm.Manifest.get_mapper();
- var properties = mapper.map_from(manifest);
- var manifest_json_element = new InvercargillJson.JsonElement.from_properties(properties);
- var manifest_json_string = manifest_json_element.stringify_pretty();
-
- // Write the updated manifest to file
- var file = File.new_for_path("MANIFEST.usm");
- var data_stream = new DataOutputStream(file.replace(null, false, FileCreateFlags.REPLACE_DESTINATION));
- data_stream.put_string(manifest_json_string);
-
- printerr("Successfully updated 'provides' property in MANIFEST.usm\n");
- }
- catch(Error e) {
- printerr(@"Error updating MANIFEST.usm: $(e.message)\n");
- printerr("Original file left unchanged. Generated provides:\n");
- print(@"$json_string\n");
- return 249;
- }
- } else {
- print(@"$json_string\n");
- }
-
- return 0;
- }
- catch(Error e) {
- printerr(@"Error scanning installation result: $(e.message)\n");
- return 249;
- }
- }
- public static Invercargill.DataStructures.Dictionary<Usm.ResourceRef, Usm.ManifestFile> autoprovides_scan_tree(string install_root, string path) throws Error {
- // Creating a new Directory object for the given folder path
- var folder = File.new_for_path(path);
- // Getting a list of all files and folders inside the directory
- var enumerator = folder.enumerate_children("*", FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
- var provides = new Invercargill.DataStructures.Dictionary<Usm.ResourceRef, Usm.ManifestFile>();
- // Looping through each file/folder and removing them
- while (true) {
- FileInfo? info = enumerator.next_file();
- if (info == null) {
- break;
- }
- // Checking if the current item is a file or a folder
- if (info.get_file_type() == FileType.REGULAR) {
- var file = folder.get_child(info.get_name());
- var true_path = file.get_path().substring(install_root.length);
- var type = guess_resource_type(true_path);
-
- var relative_path = get_relative_path_for_type(true_path, type.to_string());
- // Remove leading slash from relative_path if present
- if (relative_path.has_prefix("/")) {
- relative_path = relative_path.substring(1);
- }
-
- // Create ResourceRef and ManifestFile
- var resource_ref = new Usm.ResourceRef.with_type(type, relative_path);
- var manifest_file = new Usm.ManifestFile.from_string("as-expected");
- provides[resource_ref] = manifest_file;
- }
- else if (info.get_file_type() == FileType.SYMBOLIC_LINK) {
- var file = folder.get_child(info.get_name());
- var true_path = file.get_path().substring(install_root.length);
- var type = guess_resource_type(true_path);
-
- var relative_path = get_relative_path_for_type(true_path, type.to_string());
- // Remove leading slash from relative_path if present
- if (relative_path.has_prefix("/")) {
- relative_path = relative_path.substring(1);
- }
-
- // Create ResourceRef and ManifestFile
- var resource_ref = new Usm.ResourceRef.with_type(type, relative_path);
- var manifest_file = new Usm.ManifestFile() {
- path_base = Usm.ManifestFilePathBase.AS_EXPECTED,
- file_type = Usm.ManifestFileType.SYMBOLIC_LINK,
- destination = info.get_symlink_target()
- };
- provides[resource_ref] = manifest_file;
- }
- else if (info.get_file_type() == FileType.DIRECTORY) {
- var subfolder = folder.get_child(info.get_name());
- var sub_provides = autoprovides_scan_tree(install_root, subfolder.get_path());
- // Add all entries from subdirectory
- foreach (var entry in sub_provides) {
- provides[entry.key] = entry.value;
- }
- }
- }
-
- return provides;
- }
- private static Usm.ResourceType guess_resource_type(string path) {
- // Remove leading slash if present
- string clean_path = path.has_prefix("/") ? path.substring(1) : path;
-
- // Create a temporary Paths object to get the default paths
- var default_paths = new Usm.Paths();
-
- // Find all matching resource types and calculate directory traversals
- var matching_types = new Invercargill.DataStructures.Vector<Usm.ResourceType>();
- var traversal_counts = new Invercargill.DataStructures.Vector<int>();
-
- // Define all resource types to check
- var types_to_check = new Invercargill.Composition<Usm.ResourceType>();
- types_to_check.append(Invercargill.Iterate.these<Usm.ResourceType>(
- Usm.ResourceType.BINARY,
- Usm.ResourceType.SUPER_BINARY,
- Usm.ResourceType.LIBRARY,
- Usm.ResourceType.LIBRARY_RESOURCE,
- Usm.ResourceType.LIBRARY_EXECUTABLE,
- Usm.ResourceType.INCLUDE,
- Usm.ResourceType.APPLICATION,
- Usm.ResourceType.INFO_PAGE,
- Usm.ResourceType.MANUAL_PAGE,
- Usm.ResourceType.LOCALE,
- Usm.ResourceType.VALA_API,
- Usm.ResourceType.GOBJECT_IR,
- Usm.ResourceType.TAG,
- Usm.ResourceType.RESOURCE,
- Usm.ResourceType.PKG_CONFIG,
- Usm.ResourceType.TYPELIB,
- Usm.ResourceType.CONFIGURATION,
- Usm.ResourceType.OPTIONAL
- ));
- // If the canonlib directory is different to the regular library path, add the canonical checks
- if(paths.canonlib != paths.lib) {
- types_to_check.append(Invercargill.Iterate.these<Usm.ResourceType>(
- Usm.ResourceType.CANONICAL_LIBRARY,
- Usm.ResourceType.CANONICAL_LIBRARY_RESOURCE
- ));
- }
-
- foreach (var type in types_to_check) {
- var suggested_path = default_paths.get_suggested_base_path_for_type(type);
- // Remove the destination prefix (usually "/") to get the relative path
- var relative_suggested_path = suggested_path.has_prefix("/") ?
- suggested_path.substring(1) : suggested_path;
-
- if (clean_path.has_prefix(relative_suggested_path)) {
- var relative_path = clean_path.substring(relative_suggested_path.length);
- // Count slashes (directory traversals) in the relative path
- int traversal_count = 0;
- for (int j = 0; j < relative_path.length; j++) {
- if (relative_path[j] == '/') {
- traversal_count++;
- }
- }
-
- // Library and Library resource have the same path. Try to have actual libraries get 'lib:' type, and all others 'libres:'
- var basename = Path.get_basename(path);
- var is_library = basename.has_suffix(".so") || basename.contains(".so.") || basename.has_suffix(".a") || basename.has_suffix(".o");
- if(!is_library && (type == Usm.ResourceType.LIBRARY || type == Usm.ResourceType.CANONICAL_LIBRARY)) {
- continue;
- }
- if(is_library && (type == Usm.ResourceType.LIBRARY_RESOURCE || type == Usm.ResourceType.CANONICAL_LIBRARY_RESOURCE)) {
- continue;
- }
-
- matching_types.add(type);
- traversal_counts.add(traversal_count);
- }
- }
-
- // If no matches found, default to "rootpath"
- if (matching_types.peek_count() == 0) {
- return Usm.ResourceType.ROOT_PATH;
- }
-
- // Find the type with the fewest directory traversals
- int min_traversals = traversal_counts[0];
- Usm.ResourceType best_type = matching_types[0];
-
- for (int i = 1; i < traversal_counts.peek_count(); i++) {
- if (traversal_counts[i] < min_traversals) {
- min_traversals = traversal_counts[i];
- best_type = matching_types[i];
- }
- }
-
- return best_type;
- }
- private static string get_relative_path_for_type(string path, string type) {
- // Remove leading slash if present
- string clean_path = path.has_prefix("/") ? path.substring(1) : path;
-
- // Create a temporary Paths object to get the default paths
- var default_paths = new Usm.Paths();
-
- try {
- // Convert string type to ResourceType enum
- var resource_type = Usm.ResourceType.from_string(type);
- var suggested_path = default_paths.get_suggested_base_path_for_type(resource_type);
- // Remove the destination prefix (usually "/") to get the relative path
- var relative_suggested_path = suggested_path.has_prefix("/") ?
- suggested_path.substring(1) : suggested_path;
-
- if (clean_path.has_prefix(relative_suggested_path)) {
- return clean_path.substring(relative_suggested_path.length);
- }
- } catch (Error e) {
- // Type not found, fall through to default case
- }
-
- // Default case - return the path as-is
- return clean_path;
- }
- private int test() {
- if(manifest.is_data_package) {
- printerr(@"Data packages define no test script.\n");
- return 248;
- }
- if(build_path == null) {
- var dir = File.new_build_filename("/tmp", Uuid.string_random());
- try {
- dir.make_directory();
- }
- catch(Error e) {
- printerr(@"Could not create temporary build directory, try specifying one instead: $(e.message)\n");
- return 255;
- }
- build_path = dir.get_path();
- } else {
- // Create build directory if it doesn't exist
- var build_dir = File.new_for_path(build_path);
- if(!build_dir.query_exists()) {
- printerr(@"Creating build directory: $build_path\n");
- try {
- build_dir.make_directory_with_parents();
- }
- catch(Error e) {
- printerr(@"Could not create build directory: $(e.message)\n");
- return 255;
- }
- }
- }
- var sane = phase_dependencies_satisfied(manifest.dependencies.manage, "management", resfinder) &
- phase_dependencies_satisfied(manifest.dependencies.build, "build", resfinder);
- if(!sane) {
- printerr("Could not test manifest, missing dependencies\n");
- return 252;
- }
- try {
- // First build the package
- var build_proc = manifest.run_build(build_path, paths, SubprocessFlags.INHERIT_FDS, frac => printerr(@"Building '$(manifest.name)': $((int)(frac*100))%\r"));
- build_proc.wait_check();
- printerr("\n");
- }
- catch(Error e) {
- printerr(@"\nError running build exec: $(e.message)\n");
- return 251;
- }
- if(manifest.executables.test == null) {
- printerr(@"Manifest does not reference a test script\n");
- return 248;
- }
- try {
- var test_proc = manifest.run_test(build_path, SubprocessFlags.INHERIT_FDS);
- test_proc.wait_check();
- }
- catch(Error e) {
- printerr(@"Error running test exec: $(e.message)\n");
- return 251;
- }
- return 0;
- }
- private int validate() {
- if(build_path == null) {
- var dir = File.new_build_filename("/tmp", Uuid.string_random());
- try {
- dir.make_directory();
- }
- catch(Error e) {
- printerr(@"Could not create temporary build directory, try specifying one instead: $(e.message)\n");
- return 255;
- }
- build_path = dir.get_path();
- } else {
- // Create build directory if it doesn't exist
- var build_dir = File.new_for_path(build_path);
- if(!build_dir.query_exists()) {
- printerr(@"Creating build directory: $build_path\n");
- try {
- build_dir.make_directory_with_parents();
- }
- catch(Error e) {
- printerr(@"Could not create build directory: $(e.message)\n");
- return 255;
- }
- }
- }
- var sane = phase_dependencies_satisfied(manifest.dependencies.manage, "management", resfinder) &
- phase_dependencies_satisfied(manifest.dependencies.build, "build", resfinder) &
- phase_dependencies_satisfied(manifest.dependencies.runtime, "runtime", destresfinder);
- if(!sane) {
- printerr("Could not validate manifest, missing dependencies\n");
- return 252;
- }
- // Check for license
- if(manifest.licences == null || manifest.licences.count() == 0) {
- printerr("Warning: No licence has been provided in the manifest.\n");
- }
- // Check for markdown
- if(manifest.markdown_path == null) {
- printerr("Warning: No markdown description has been provided in the manifest.\n");
- }
- // Check for url
- if(manifest.url == null) {
- printerr("Warning: No URL has been provided in the manifest.\n");
- }
- // Data packages define no build or install execs; their provides resolve
- // directly from the source tree, so validate them there and stop
- if(manifest.is_data_package) {
- return validate_data_package();
- }
- try {
- // Run build
- var build_proc = manifest.run_build(build_path, paths, SubprocessFlags.STDOUT_SILENCE, frac => printerr(@"Building '$(manifest.name)': $((int)(frac*100))%\r"));
- build_proc.wait_check();
- printerr("\n");
- }
- catch(Error e) {
- printerr(@"\nError running build exec: $(e.message)\n");
- return 251;
- }
- var install_dir = File.new_build_filename("/tmp", Uuid.string_random());
- if(manifest.executables.install != null) {
- try {
- install_dir.make_directory();
- }
- catch(Error e) {
- printerr(@"Could not create temporary install directory: $(e.message)\n");
- return 250;
- }
- try {
- manifest.run_install(build_path, install_dir.get_path(), paths, Usm.InstallType.FRESH, SubprocessFlags.STDOUT_SILENCE).wait_check();
- }
- catch(Error e) {
- printerr(@"Error running install exec: $(e.message)\n");
- return 249;
- }
- }
- else {
- printerr("Warning: No install script specified, cannot validate installation.\n");
- return 0;
- }
- // Validate each expected resource exists in its expected location
- var missing_resources = new Invercargill.DataStructures.Vector<Usm.ResourceRef>();
- foreach (var expected in manifest.provides) {
- bool found = false;
- string expected_path = "";
-
- try {
- switch(expected.value.path_base) {
- case Usm.ManifestFilePathBase.AS_EXPECTED:
- // For as-expected, check in install directory at suggested path
- var install_paths = paths.clone();
- install_paths.destination = install_dir.get_path();
- expected_path = install_paths.get_suggested_path_for_resource(expected.key);
- var file = File.new_for_path(expected_path);
- found = file.query_exists();
- break;
-
- case Usm.ManifestFilePathBase.BUILD:
- // For build:, check in build directory at specified path
- expected_path = Path.build_filename(build_path, expected.value.path ?? "");
- var file = File.new_for_path(expected_path);
- found = file.query_exists();
- break;
-
- case Usm.ManifestFilePathBase.SOURCE:
- // For source:, check in source directory at specified path
- expected_path = Path.build_filename(Environment.get_current_dir(), expected.value.path ?? "");
- var file = File.new_for_path(expected_path);
- found = file.query_exists();
- break;
-
- case Usm.ManifestFilePathBase.INSTALL:
- // For install:, check in install directory at specified path
- expected_path = Path.build_filename(install_dir.get_path(), expected.value.path ?? "");
- var file = File.new_for_path(expected_path);
- found = file.query_exists();
- break;
-
- default:
- assert_not_reached();
- }
- }
- catch(Error e) {
- printerr(@"Error validating resource $(expected.key.to_string()): $(e.message)\n");
- found = false;
- }
-
- if(!found) {
- missing_resources.add(expected.key);
- }
- }
-
- if(missing_resources.any()) {
- printerr("Error: Expected resources not found:\n");
- foreach (var resource in missing_resources) {
- var expected_resource = manifest.provides[resource];
- string location_desc = "";
- switch(expected_resource.path_base) {
- case Usm.ManifestFilePathBase.AS_EXPECTED:
- location_desc = "at expected install location";
- break;
- case Usm.ManifestFilePathBase.BUILD:
- location_desc = @"in build directory at \"$(expected_resource.path ?? "")\"";
- break;
- case Usm.ManifestFilePathBase.SOURCE:
- location_desc = @"in source directory at \"$(expected_resource.path ?? "")\"";
- break;
- case Usm.ManifestFilePathBase.INSTALL:
- location_desc = @"in install directory at \"$(expected_resource.path ?? "")\"";
- break;
- default:
- location_desc = "at unknown location";
- break;
- }
- printerr(@" - $(resource.to_string()) ($location_desc)\n");
- }
- return 248;
- }
-
- // Scan for unexpected resources in the install directory
- try {
- var installed_resources = autoprovides_scan_tree(install_dir.get_path(), install_dir.get_path());
- var expected_resources = manifest.provides;
-
- // Check for unexpected resources
- var unexpected_resources = new Invercargill.DataStructures.Vector<Usm.ResourceRef>();
- foreach (var installed in installed_resources) {
- bool found = false;
- foreach (var expected in expected_resources) {
- if(installed.key.to_string() == expected.key.to_string()) {
- found = true;
- break;
- }
- }
- if(!found) {
- unexpected_resources.add(installed.key);
- }
- }
-
- if(unexpected_resources.any()) {
- printerr("Warning: Unexpected resources found in install directory:\n");
- foreach (var resource in unexpected_resources) {
- printerr(@" - $(resource.to_string())\n");
- }
- }
- }
- catch(Error e) {
- printerr(@"Error validating installation result: $(e.message)\n");
- return 247;
- }
- printerr("Manifest validation completed successfully.\n");
- return 0;
- }
- /**
- * Validates a data package: every provide must exist in the source tree at
- * its declared path; no build or install staging is involved.
- */
- private int validate_data_package() {
- var missing_resources = new Invercargill.DataStructures.Vector<Usm.ResourceRef>();
- foreach (var expected in manifest.provides) {
- var expected_path = Path.build_filename(Environment.get_current_dir(), expected.value.path ?? "");
- if(!File.new_for_path(expected_path).query_exists()) {
- missing_resources.add(expected.key);
- }
- }
- if(missing_resources.any()) {
- printerr("Error: Expected resources not found in the source tree:\n");
- foreach (var resource in missing_resources) {
- printerr(@" - $(resource.to_string()) (in source directory at \"$(manifest.provides[resource].path ?? "")\")\n");
- }
- return 248;
- }
- printerr("Manifest validation completed successfully.\n");
- return 0;
- }
|