瀏覽代碼

feat(build): add ninja-style progress reporting for builds

Implements ninja-style progress reporting during package builds by:
- Adding a new "ninjaStyleProgress" manifest flag to enable progress parsing
- Creating a ProgressDelegate callback system for real-time progress updates
- Parsing "[current/total]" patterns from build output to calculate progress
- Updating CLI install and autoprovides commands to display build progress
- Integrating progress reporting into the Transaction build process

The progress monitoring runs in a separate thread and safely handles
errors without interrupting the build process.
clanker 1 月之前
父節點
當前提交
042afdd48b
共有 4 個文件被更改,包括 90 次插入11 次删除
  1. 1 1
      MANIFEST.usm
  2. 4 3
      src/cli/Manifest.vala
  3. 81 4
      src/lib/Manifest.vala
  4. 4 3
      src/lib/Transaction.vala

+ 1 - 1
MANIFEST.usm

@@ -3,7 +3,7 @@
   "version": "0.0.1",
   "summary": "Universal Source Manifest",
   "licences": [ {"name": "GPLv3", "category": "libre", "text": "src/LICENSE"} ],
-  "flags": [],
+  "flags": ["ninjaStyleProgress"],
   "provides": {
     "bin:usm": "as-expected",
     "lib:libusm.so": "as-expected",

+ 4 - 3
src/cli/Manifest.vala

@@ -148,7 +148,7 @@ private int install() {
     }
 
     try {
-        var proc = manifest.run_build(build_path, paths, SubprocessFlags.INHERIT_FDS);
+        var proc = manifest.run_build(build_path, paths, SubprocessFlags.INHERIT_FDS, frac => printerr(@"Building '$(manifest.name)': $((int)(frac*100))%\r"));
         proc.wait_check();
     }
     catch(Error e) {
@@ -367,11 +367,12 @@ private int autoprovides() {
     }
 
     try {
-        var proc = manifest.run_build(build_path, paths, SubprocessFlags.STDOUT_SILENCE);
+        var proc = manifest.run_build(build_path, paths, SubprocessFlags.STDOUT_SILENCE, frac => printerr(@"Building '$(manifest.name)': $((int)(frac*100))%\r"));
         proc.wait_check();
+        printerr("\n");
     }
     catch(Error e) {
-        printerr(@"Error running build exec: $(e.message)\n");
+        printerr(@"\nError running build exec: $(e.message)\n");
         return 251;
     }
 

+ 81 - 4
src/lib/Manifest.vala

@@ -133,11 +133,81 @@ namespace Usm {
         }
 
         
-        public Subprocess run_build(string build_path, Paths paths, SubprocessFlags flags) throws Error {
+        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);
             paths.set_envs();
-            var proc = new Subprocess.newv(new string[] { path, build_path }, flags);
-            return proc;
+            
+            // Check if NINJA_STYLE_PROGRESS flag is set and progress delegate is provided
+            if (this.flags.contains(ManifestFlag.NINJA_STYLE_PROGRESS) && progress_delegate != null) {
+                // Set up subprocess to capture STDOUT for progress parsing
+                var modified_flags = flags;
+                // Ensure STDOUT is not silenced when we need to parse progress
+                if ((modified_flags & SubprocessFlags.STDOUT_SILENCE) != 0) {
+                    modified_flags = modified_flags & ~SubprocessFlags.STDOUT_SILENCE;
+                }
+                modified_flags = modified_flags | SubprocessFlags.STDOUT_PIPE;
+                
+                var proc = new Subprocess.newv(new string[] { path, build_path }, modified_flags);
+                
+                // Start a new thread to monitor STDOUT for progress information
+                ThreadFunc<void> progress_thread_func = () => {
+                    try {
+                        var stdout_pipe = proc.get_stdout_pipe();
+                        if (stdout_pipe != null) {
+                            var dis = new DataInputStream(stdout_pipe);
+                            string line;
+                            
+                            // Read lines from STDOUT until the process ends
+                            while ((line = dis.read_line(null)) != null) {
+                                // Look for Ninja progress pattern: "[x/x] "
+                                if (line.has_prefix("[")) {
+                                    var end_bracket = line.index_of("]");
+                                    if (end_bracket > 1) {
+                                        var progress_str = line.substring(1, end_bracket - 1);
+                                        var parts = progress_str.split("/");
+                                        
+                                        if (parts.length == 2) {
+                                            int current_task = 0;
+                                            int total_tasks = 0;
+                                            
+                                            // Parse the current and total task numbers
+                                            if (int.try_parse(parts[0], out current_task) &&
+                                                int.try_parse(parts[1], out total_tasks) &&
+                                                total_tasks > 0) {
+                                                
+                                                // Calculate progress as a float between 0.0 and 1.0
+                                                float progress = (float)current_task / (float)total_tasks;
+                                                
+                                                // Ensure progress is within valid bounds
+                                                progress = float.max(0.0f, float.min(1.0f, progress));
+                                                
+                                                // Call the progress delegate with the calculated progress
+                                                progress_delegate(progress);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    } catch (Error e) {
+                        // Log any errors during progress monitoring but don't fail the build
+                        warning(@"Error monitoring build progress: $(e.message)");
+                    }
+                };
+                
+                try {
+                    // Start the progress monitoring thread
+                    new Thread<void>(null, progress_thread_func);
+                } catch (Error e) {
+                    // If we can't start the thread, log a warning but continue with the build
+                    warning(@"Failed to start progress monitoring thread: $(e.message)");
+                }
+                
+                return proc;
+            } else {
+                var proc = new Subprocess.newv(new string[] { path, build_path }, flags);
+                return proc;
+            }
         }
 
         public Subprocess? run_rebuild(string build_path, SubprocessFlags flags) throws Error {
@@ -369,9 +439,12 @@ namespace Usm {
         }
     }
 
+    public delegate void ProgressDelegate(float progress);
+
     public enum ManifestFlag {
         BUILD_IN_SOURCE_TREE,
-        SET_MANIFEST_PROPERTY_ENVS;
+        SET_MANIFEST_PROPERTY_ENVS,
+        NINJA_STYLE_PROGRESS;
 
         public string to_string() {
             switch (this) {
@@ -379,6 +452,8 @@ namespace Usm {
                     return "buildInSourceTree";
                 case ManifestFlag.SET_MANIFEST_PROPERTY_ENVS:
                     return "setManifestPropertyEnvs";
+                case ManifestFlag.NINJA_STYLE_PROGRESS:
+                    return "ninjaStyleProgress";
                 default:
                     assert_not_reached();
             }
@@ -390,6 +465,8 @@ namespace Usm {
                     return ManifestFlag.BUILD_IN_SOURCE_TREE;
                 case "setManifestPropertyEnvs":
                     return ManifestFlag.SET_MANIFEST_PROPERTY_ENVS;
+                case "ninjaStyleProgress":
+                    return ManifestFlag.NINJA_STYLE_PROGRESS;
                 default:
                     throw new ManifestError.INVALID_FLAG(@"Unknown flag \"$str\".");
             }

+ 4 - 3
src/lib/Transaction.vala

@@ -206,9 +206,10 @@ namespace Usm {
             var manifest = new Usm.Manifest.from_file("MANIFEST.usm");
 
             // Build package
-            var build_proc = manifest.run_build(build_dir, paths, SubprocessFlags.STDOUT_SILENCE);
-            build_proc.wait_check();   
-            report_progress(TransactionTask.BUILDING, 1.0f);
+            var build_proc = manifest.run_build(build_dir, paths, SubprocessFlags.STDOUT_SILENCE, (progress) => {
+                report_progress(TransactionTask.BUILDING, progress);
+            });
+            build_proc.wait_check();
         }
 
         private void remove_package(CachedPackage package) throws Error {