Quellcode durchsuchen

feat(manifest): add simpleBuildEnvironment flag

Add a new manifest flag that copies the full source tree to the build directory before executing build-related scripts and runs them from the build directory instead of the source directory. This provides a clean, isolated build environment particularly useful for build systems that expect to run from the build directory or projects that need to write temporary files during build.

The implementation includes:
- New SIMPLE_BUILD_ENVIRONMENT flag in ManifestFlag enum
- copy_directory() helper method to replicate source tree
- Modified execution methods (build, rebuild, install, post_install, test) to handle working directory changes
- Updated documentation across all relevant files
- Proper working directory restoration after subprocess execution
clanker vor 1 Monat
Ursprung
Commit
e12adcc7f7

+ 11 - 0
README.md

@@ -26,6 +26,9 @@
       "bin:bash"
     ]
   },
+  "flags": [
+    "simpleBuildEnvironment"
+  ],
   "execs": {
     "install": "usp-exec/install",
     "remove": "usp-exec/remove",
@@ -50,6 +53,14 @@
 }
 ```
 
+## Manifest flags
+
+USM supports several flags that modify the build and installation behavior:
+
+- `"buildInSourceTree"`: Tell USM to not create a separate build directory, and compile the package "in place".
+- `"setManifestPropertyEnvs"`: Sets environment variables based on manifest properties (TODO: document specifics).
+- `"simpleBuildEnvironment"`: Copy the full source tree to the build directory before running the build exec, and execute all build-related scripts (build, install, rebuild, test, postInstall) from the build directory instead of the source directory. This provides a clean, isolated build environment.
+
 ## Resource types
 
 - "rootpath": Generic resource: file on root filesystem without leading "/"

+ 2 - 1
SPECIFICATION.md

@@ -65,7 +65,8 @@ The USM manifest file is contains a JSON object with the following properties:
 - `flags` (string array, required): A list of flags for this manifest, each string in the array can be one of the following:
     - `"buildInSourceTree"` to tell usm to not create a separate build directory, and compile the package "in place".
     - `"setManifestPropertyEnvs"` idk TODO
-- `execs` (object, required): An object describing executable scripts for different phases of the package lifecycle (see [Executable Scripts](#executable-scripts)). It has the following properties :
+    - `"simpleBuildEnvironment"` to copy the full source tree to the build directory before running the build exec, and execute all build-related scripts from the build directory instead of the source directory.
+    - `execs` (object, required): An object describing executable scripts for different phases of the package lifecycle (see [Executable Scripts](#executable-scripts)). It has the following properties :
     - `build` (string, required): Path to the build script relative to the package root.
     - `install` (string, optional): Path to the install script relative to the package root.
     - `remove` (string, optional): Path to the removal script relative to the package root.

+ 38 - 0
slopdocs/structure.usm.manifest.executable-scripts.md

@@ -11,6 +11,10 @@ Required script that compiles source code into binaries.
 **Arguments**: `[build-directory]`
 - `build-directory`: Path where build output should be placed
 
+**Working Directory**:
+- By default: Source directory
+- With `simpleBuildEnvironment` flag: Build directory (source tree is copied first)
+
 **Environment Variables**:
 - `USM_DESTDIR`, `USM_PREFIX`, `USM_BINDIR`, etc. are set
 
@@ -21,6 +25,11 @@ set -e
 
 build_dir=$1
 
+# With simpleBuildEnvironment flag, we're already in build directory
+# Without the flag, we need to change to build directory
+if [ ! -d "$build_dir" ]; then
+  mkdir -p "$build_dir"
+fi
 cd ${build_dir}
 meson setup ${src_dir} --prefix=${PREFIX} --libdir=${LIBDIR}
 ninja
@@ -34,6 +43,10 @@ Optional script that installs built files to destination directory.
 - `install-directory`: Path where files should be installed (DESTDIR)
 - `install-type`: "fresh", "upgrade", or "downgrade"
 
+**Working Directory**:
+- By default: Source directory
+- With `simpleBuildEnvironment` flag: Build directory
+
 **Environment Variables**:
 - `DESTDIR` is overridden to install-directory
 - Other USM variables are available
@@ -47,6 +60,8 @@ build_dir=$1
 install_dir=$2
 install_type=$3
 
+# With simpleBuildEnvironment flag, we're already in build directory
+# Without the flag, we need to change to build directory
 cd ${build_dir}
 meson install --destdir ${install_dir}
 ```
@@ -58,6 +73,10 @@ Optional script that runs after USM installs resources to system.
 - `build-directory`: Path containing build output
 - `install-type`: "fresh", "upgrade", or "downgrade"
 
+**Working Directory**:
+- By default: Source directory
+- With `simpleBuildEnvironment` flag: Build directory
+
 **Use Case**: System integration, cache generation, service registration
 
 **Example**:
@@ -136,6 +155,10 @@ Optional script that runs tests after build but before installation.
 **Arguments**: `[build-directory]`
 - `build-directory`: Path containing build output
 
+**Working Directory**:
+- By default: Source directory
+- With `simpleBuildEnvironment` flag: Build directory
+
 **Environment Variables**:
 - `USM_DESTDIR`, `USM_PREFIX`, `USM_BINDIR`, etc. are set
 
@@ -148,6 +171,8 @@ set -e
 
 build_dir=$1
 
+# With simpleBuildEnvironment flag, we're already in build directory
+# Without the flag, we need to change to build directory
 cd ${build_dir}
 # Run unit tests
 ./test-suite
@@ -235,6 +260,19 @@ cmake ${src_dir} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_INSTALL_LIBDIR=${LIBDI
 make
 ```
 
+## Special Manifest Flags
+
+### simpleBuildEnvironment
+When the `simpleBuildEnvironment` flag is set in the manifest:
+- The full source tree is copied to the build directory before executing any build-related scripts
+- All build-related scripts (build, install, test, postInstall) are executed from the build directory instead of the source directory
+- This provides a clean, isolated build environment where scripts can safely write to the current directory
+
+This flag is particularly useful for:
+- Build systems that expect to run from the build directory
+- Projects that need to write temporary files during build
+- Simplifying build scripts by eliminating the need to change directories
+
 ## Progress Reporting
 
 When using `ninjaStyleProgress` flag, USM parses build output for progress:

+ 2 - 1
slopdocs/structure.usm.manifest.md

@@ -101,9 +101,10 @@ Package behavior flags.
 - `"buildInSourceTree"`: Build in source directory
 - `"setManifestPropertyEnvs"`: Set manifest properties as environment variables
 - `"ninjaStyleProgress"`: Parse Ninja-style progress output
+- `"simpleBuildEnvironment"`: Copy full source tree to build directory before building and execute all build-related scripts from build directory
 
 ```json
-"flags": ["ninjaStyleProgress"]
+"flags": ["simpleBuildEnvironment"]
 ```
 
 ## Optional Fields

+ 117 - 10
src/lib/Manifest.vala

@@ -146,7 +146,27 @@ namespace Usm {
 
         
         public Subprocess run_build(string build_path, Paths paths, SubprocessFlags flags, ProgressDelegate? progress_delegate = null) throws Error {
-            var path = Path.build_filename(Environment.get_current_dir(), executables.build);
+            // Handle SIMPLE_BUILD_ENVIRONMENT flag
+            string original_working_dir = Environment.get_current_dir();
+            string working_dir = original_working_dir;
+            string effective_build_path = build_path;
+            
+            if (this.flags.contains(ManifestFlag.SIMPLE_BUILD_ENVIRONMENT)) {
+                // Copy the full source tree to the build directory
+                var source_dir = File.new_for_path(working_dir);
+                var build_dir = File.new_for_path(build_path);
+                
+                // Copy all files from source to build directory
+                copy_directory(source_dir, build_dir);
+                
+                // Use build directory as working directory
+                working_dir = build_path;
+            }
+            
+            var path = Path.build_filename(working_dir, executables.build);
+            
+            // Change to the working directory for subprocess execution
+            Environment.set_current_dir(working_dir);
             paths.set_envs();
             
             // Check if NINJA_STYLE_PROGRESS flag is set and progress delegate is provided
@@ -159,7 +179,7 @@ namespace Usm {
                 }
                 modified_flags = modified_flags | SubprocessFlags.STDOUT_PIPE;
                 
-                var proc = new Subprocess.newv(new string[] { path, build_path }, modified_flags);
+                var proc = new Subprocess.newv(new string[] { path, effective_build_path }, modified_flags);
                 
                 // Start a new thread to monitor STDOUT for progress information
                 ThreadFunc<void> progress_thread_func = () => {
@@ -215,9 +235,16 @@ namespace Usm {
                     warning(@"Failed to start progress monitoring thread: $(e.message)");
                 }
                 
+                // Restore original working directory after subprocess completes
+                Environment.set_current_dir(original_working_dir);
+                
                 return proc;
             } else {
-                var proc = new Subprocess.newv(new string[] { path, build_path }, flags);
+                var proc = new Subprocess.newv(new string[] { path, effective_build_path }, flags);
+                
+                // Restore original working directory after subprocess completes
+                Environment.set_current_dir(original_working_dir);
+                
                 return proc;
             }
         }
@@ -226,8 +253,20 @@ namespace Usm {
             if(executables.rebuild == null) {
                 return null;
             }
-            var path = Path.build_filename(Environment.get_current_dir(), executables.rebuild);
+            string original_working_dir = Environment.get_current_dir();
+            string working_dir = original_working_dir;
+            if (this.flags.contains(ManifestFlag.SIMPLE_BUILD_ENVIRONMENT)) {
+                working_dir = build_path;
+            }
+            var path = Path.build_filename(working_dir, executables.rebuild);
+            
+            // Change to the working directory for subprocess execution
+            Environment.set_current_dir(working_dir);
             var proc = new Subprocess.newv(new string[] { path, build_path }, flags);
+            
+            // Restore original working directory after subprocess completes
+            Environment.set_current_dir(original_working_dir);
+            
             return proc;
         }
 
@@ -235,7 +274,9 @@ namespace Usm {
             if(executables.acquire == null) {
                 return null;
             }
-            var path = Path.build_filename(Environment.get_current_dir(), executables.acquire);
+            string working_dir = Environment.get_current_dir();
+            // Note: acquire doesn't have a build_path parameter, so it can't use SIMPLE_BUILD_ENVIRONMENT
+            var path = Path.build_filename(working_dir, executables.acquire);
             var proc = new Subprocess.newv(new string[] { path }, flags);
             return proc;
         }
@@ -244,13 +285,25 @@ namespace Usm {
             if(executables.install == null) {
                 return null;
             }
-            var path = Path.build_filename(Environment.get_current_dir(), executables.install);
+            string original_working_dir = Environment.get_current_dir();
+            string working_dir = original_working_dir;
+            if (this.flags.contains(ManifestFlag.SIMPLE_BUILD_ENVIRONMENT)) {
+                working_dir = build_path;
+            }
+            var path = Path.build_filename(working_dir, executables.install);
 
             // Override destination environment variable
             var new_paths = paths.clone();
             new_paths.destination = install_path;
+            
+            // Change to the working directory for subprocess execution
+            Environment.set_current_dir(working_dir);
             new_paths.set_envs();
             var proc = new Subprocess.newv(new string[] { path, build_path, install_path, type.to_string() }, flags);
+            
+            // Restore original working directory after subprocess completes
+            Environment.set_current_dir(original_working_dir);
+            
             return proc;
         }
 
@@ -258,8 +311,20 @@ namespace Usm {
             if(executables.post_install == null) {
                 return null;
             }
-            var path = Path.build_filename(Environment.get_current_dir(), executables.post_install);
+            string original_working_dir = Environment.get_current_dir();
+            string working_dir = original_working_dir;
+            if (this.flags.contains(ManifestFlag.SIMPLE_BUILD_ENVIRONMENT)) {
+                working_dir = build_path;
+            }
+            var path = Path.build_filename(working_dir, executables.post_install);
+            
+            // Change to the working directory for subprocess execution
+            Environment.set_current_dir(working_dir);
             var proc = new Subprocess.newv(new string[] { path, build_path, type.to_string() }, flags);
+            
+            // Restore original working directory after subprocess completes
+            Environment.set_current_dir(original_working_dir);
+            
             return proc;
         }
 
@@ -267,7 +332,9 @@ namespace Usm {
             if(executables.remove == null) {
                 return null;
             }
-            var path = Path.build_filename(Environment.get_current_dir(), executables.remove);
+            string working_dir = Environment.get_current_dir();
+            // Note: remove doesn't have a build_path parameter, so it can't use SIMPLE_BUILD_ENVIRONMENT
+            var path = Path.build_filename(working_dir, executables.remove);
             var proc = new Subprocess.newv(new string[] { path, type.to_string() }, flags);
             return proc;
         }
@@ -276,8 +343,20 @@ namespace Usm {
             if(executables.test == null) {
                 return null;
             }
-            var path = Path.build_filename(Environment.get_current_dir(), executables.test);
+            string original_working_dir = Environment.get_current_dir();
+            string working_dir = original_working_dir;
+            if (this.flags.contains(ManifestFlag.SIMPLE_BUILD_ENVIRONMENT)) {
+                working_dir = build_path;
+            }
+            var path = Path.build_filename(working_dir, executables.test);
+            
+            // Change to the working directory for subprocess execution
+            Environment.set_current_dir(working_dir);
             var proc = new Subprocess.newv(new string[] { path, build_path }, flags);
+            
+            // Restore original working directory after subprocess completes
+            Environment.set_current_dir(original_working_dir);
+            
             return proc;
         }
 
@@ -393,6 +472,29 @@ namespace Usm {
             }
         }
 
+        private void copy_directory(File source, File destination) throws Error {
+            // Ensure destination directory exists
+            if (!destination.query_exists()) {
+                destination.make_directory_with_parents();
+            }
+
+            var enumerator = source.enumerate_children(FileAttribute.STANDARD_NAME + "," + FileAttribute.STANDARD_TYPE, FileQueryInfoFlags.NONE);
+            FileInfo file_info;
+
+            while ((file_info = enumerator.next_file()) != null) {
+                var source_child = source.get_child(file_info.get_name());
+                var destination_child = destination.get_child(file_info.get_name());
+
+                if (file_info.get_file_type() == FileType.DIRECTORY) {
+                    // Recursively copy subdirectories
+                    copy_directory(source_child, destination_child);
+                } else {
+                    // Copy files
+                    source_child.copy(destination_child, FileCopyFlags.OVERWRITE | FileCopyFlags.ALL_METADATA);
+                }
+            }
+        }
+
     }
 
 
@@ -465,7 +567,8 @@ namespace Usm {
     public enum ManifestFlag {
         BUILD_IN_SOURCE_TREE,
         SET_MANIFEST_PROPERTY_ENVS,
-        NINJA_STYLE_PROGRESS;
+        NINJA_STYLE_PROGRESS,
+        SIMPLE_BUILD_ENVIRONMENT;
 
         public string to_string() {
             switch (this) {
@@ -475,6 +578,8 @@ namespace Usm {
                     return "setManifestPropertyEnvs";
                 case ManifestFlag.NINJA_STYLE_PROGRESS:
                     return "ninjaStyleProgress";
+                case ManifestFlag.SIMPLE_BUILD_ENVIRONMENT:
+                    return "simpleBuildEnvironment";
                 default:
                     assert_not_reached();
             }
@@ -488,6 +593,8 @@ namespace Usm {
                     return ManifestFlag.SET_MANIFEST_PROPERTY_ENVS;
                 case "ninjaStyleProgress":
                     return ManifestFlag.NINJA_STYLE_PROGRESS;
+                case "simpleBuildEnvironment":
+                    return ManifestFlag.SIMPLE_BUILD_ENVIRONMENT;
                 default:
                     throw new ManifestError.INVALID_FLAG(@"Unknown flag \"$str\".");
             }