| author | |
| committer | |
| log | fdd6c31e8b25f9eed81c1e78fa71eca17fd29f68 |
| tree | 61686eb9bc1399946d702e2fe024b6bf259f5b44 |
| parent | c78f996ff986b8843f328e1f083547c538ac865b |
| parent | eee5400b7dc37845ea5f42e0841320953e7852b2 |
| signature |
std.Build: fix `Compile.installHeader` behavior, add `WriteFile.addCopyDirectory`14 files changed, 542 insertions(+), 140 deletions(-)
lib/std/Build.zig+13-12| ... | @@ -1568,23 +1568,24 @@ pub fn addObjCopy(b: *Build, source: LazyPath, options: Step.ObjCopy.Options) *S | ... | @@ -1568,23 +1568,24 @@ pub fn addObjCopy(b: *Build, source: LazyPath, options: Step.ObjCopy.Options) *S |
| 1568 | return Step.ObjCopy.create(b, source, options); | 1568 | return Step.ObjCopy.create(b, source, options); |
| 1569 | } | 1569 | } |
| 1570 | 1570 | ||
| 1571 | ///`dest_rel_path` is relative to install prefix path | 1571 | /// `dest_rel_path` is relative to install prefix path |
| 1572 | pub fn addInstallFile(self: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { | 1572 | pub fn addInstallFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { |
| 1573 | return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path); | 1573 | return b.addInstallFileWithDir(source, .prefix, dest_rel_path); |
| 1574 | } | 1574 | } |
| 1575 | 1575 | ||
| 1576 | ///`dest_rel_path` is relative to bin path | 1576 | /// `dest_rel_path` is relative to bin path |
| 1577 | pub fn addInstallBinFile(self: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { | 1577 | pub fn addInstallBinFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { |
| 1578 | return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path); | 1578 | return b.addInstallFileWithDir(source, .bin, dest_rel_path); |
| 1579 | } | 1579 | } |
| 1580 | 1580 | ||
| 1581 | ///`dest_rel_path` is relative to lib path | 1581 | /// `dest_rel_path` is relative to lib path |
| 1582 | pub fn addInstallLibFile(self: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { | 1582 | pub fn addInstallLibFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { |
| 1583 | return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path); | 1583 | return b.addInstallFileWithDir(source, .lib, dest_rel_path); |
| 1584 | } | 1584 | } |
| 1585 | 1585 | ||
| 1586 | pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *Step.InstallFile { | 1586 | /// `dest_rel_path` is relative to header path |
| 1587 | return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); | 1587 | pub fn addInstallHeaderFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { |
| 1588 | return b.addInstallFileWithDir(source, .header, dest_rel_path); | ||
| 1588 | } | 1589 | } |
| 1589 | 1590 | ||
| 1590 | pub fn addInstallFileWithDir( | 1591 | pub fn addInstallFileWithDir( |
| ... | @@ -1593,7 +1594,7 @@ pub fn addInstallFileWithDir( | ... | @@ -1593,7 +1594,7 @@ pub fn addInstallFileWithDir( |
| 1593 | install_dir: InstallDir, | 1594 | install_dir: InstallDir, |
| 1594 | dest_rel_path: []const u8, | 1595 | dest_rel_path: []const u8, |
| 1595 | ) *Step.InstallFile { | 1596 | ) *Step.InstallFile { |
| 1596 | return Step.InstallFile.create(self, source.dupe(self), install_dir, dest_rel_path); | 1597 | return Step.InstallFile.create(self, source, install_dir, dest_rel_path); |
| 1597 | } | 1598 | } |
| 1598 | 1599 | ||
| 1599 | pub fn addInstallDirectory(self: *Build, options: Step.InstallDir.Options) *Step.InstallDir { | 1600 | pub fn addInstallDirectory(self: *Build, options: Step.InstallDir.Options) *Step.InstallDir { |
lib/std/Build/Module.zig+6-15| ... | @@ -265,8 +265,7 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void { | ... | @@ -265,8 +265,7 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void { |
| 265 | for (dependee.link_objects.items) |link_object| switch (link_object) { | 265 | for (dependee.link_objects.items) |link_object| switch (link_object) { |
| 266 | .other_step => |compile| { | 266 | .other_step => |compile| { |
| 267 | addStepDependencies(m, dependee, &compile.step); | 267 | addStepDependencies(m, dependee, &compile.step); |
| 268 | for (compile.installed_headers.items) |install_step| | 268 | addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree()); |
| 269 | addStepDependenciesOnly(m, install_step); | ||
| 270 | }, | 269 | }, |
| 271 | 270 | ||
| 272 | .static_path, | 271 | .static_path, |
| ... | @@ -691,20 +690,14 @@ pub fn appendZigProcessFlags( | ... | @@ -691,20 +690,14 @@ pub fn appendZigProcessFlags( |
| 691 | }, | 690 | }, |
| 692 | .other_step => |other| { | 691 | .other_step => |other| { |
| 693 | if (other.generated_h) |header| { | 692 | if (other.generated_h) |header| { |
| 694 | try zig_args.append("-isystem"); | 693 | try zig_args.appendSlice(&.{ "-isystem", std.fs.path.dirname(header.getPath()).? }); |
| 695 | try zig_args.append(std.fs.path.dirname(header.path.?).?); | ||
| 696 | } | 694 | } |
| 697 | if (other.installed_headers.items.len > 0) { | 695 | if (other.installed_headers_include_tree) |include_tree| { |
| 698 | try zig_args.append("-I"); | 696 | try zig_args.appendSlice(&.{ "-I", include_tree.generated_directory.getPath() }); |
| 699 | try zig_args.append(b.pathJoin(&.{ | ||
| 700 | other.step.owner.install_prefix, "include", | ||
| 701 | })); | ||
| 702 | } | 697 | } |
| 703 | }, | 698 | }, |
| 704 | .config_header_step => |config_header| { | 699 | .config_header_step => |config_header| { |
| 705 | const full_file_path = config_header.output_file.path.?; | 700 | try zig_args.appendSlice(&.{ "-I", std.fs.path.dirname(config_header.output_file.getPath()).? }); |
| 706 | const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len]; | ||
| 707 | try zig_args.appendSlice(&.{ "-I", header_dir_path }); | ||
| 708 | }, | 701 | }, |
| 709 | } | 702 | } |
| 710 | } | 703 | } |
| ... | @@ -752,9 +745,7 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void { | ... | @@ -752,9 +745,7 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void { |
| 752 | m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM"); | 745 | m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM"); |
| 753 | m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM"); | 746 | m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM"); |
| 754 | 747 | ||
| 755 | for (other.installed_headers.items) |install_step| { | 748 | addLazyPathDependenciesOnly(m, other.getEmittedIncludeTree()); |
| 756 | addStepDependenciesOnly(m, install_step); | ||
| 757 | } | ||
| 758 | } | 749 | } |
| 759 | 750 | ||
| 760 | fn requireKnownTarget(m: *Module) std.Target { | 751 | fn requireKnownTarget(m: *Module) std.Target { |
lib/std/Build/Step/Compile.zig+163-66| ... | @@ -59,7 +59,13 @@ test_runner: ?[]const u8, | ... | @@ -59,7 +59,13 @@ test_runner: ?[]const u8, |
| 59 | test_server_mode: bool, | 59 | test_server_mode: bool, |
| 60 | wasi_exec_model: ?std.builtin.WasiExecModel = null, | 60 | wasi_exec_model: ?std.builtin.WasiExecModel = null, |
| 61 | 61 | ||
| 62 | installed_headers: ArrayList(*Step), | 62 | installed_headers: ArrayList(HeaderInstallation), |
| 63 | |||
| 64 | /// This step is used to create an include tree that dependent modules can add to their include | ||
| 65 | /// search paths. Installed headers are copied to this step. | ||
| 66 | /// This step is created the first time a module links with this artifact and is not | ||
| 67 | /// created otherwise. | ||
| 68 | installed_headers_include_tree: ?*Step.WriteFile = null, | ||
| 63 | 69 | ||
| 64 | // keep in sync with src/Compilation.zig:RcIncludes | 70 | // keep in sync with src/Compilation.zig:RcIncludes |
| 65 | /// Behavior of automatic detection of include directories when compiling .rc files. | 71 | /// Behavior of automatic detection of include directories when compiling .rc files. |
| ... | @@ -249,6 +255,90 @@ pub const Kind = enum { | ... | @@ -249,6 +255,90 @@ pub const Kind = enum { |
| 249 | @"test", | 255 | @"test", |
| 250 | }; | 256 | }; |
| 251 | 257 | ||
| 258 | pub const HeaderInstallation = union(enum) { | ||
| 259 | file: File, | ||
| 260 | directory: Directory, | ||
| 261 | |||
| 262 | pub const File = struct { | ||
| 263 | source: LazyPath, | ||
| 264 | dest_rel_path: []const u8, | ||
| 265 | |||
| 266 | pub fn dupe(self: File, b: *std.Build) File { | ||
| 267 | // 'path' lazy paths are relative to the build root of some step, inferred from the step | ||
| 268 | // in which they are used. This means that we can't dupe such paths, because they may | ||
| 269 | // come from dependencies with their own build roots and duping the paths as is might | ||
| 270 | // cause the build script to search for the file relative to the wrong root. | ||
| 271 | // As a temporary workaround, we convert build root-relative paths to absolute paths. | ||
| 272 | // If/when the build-root relative paths are updated to encode which build root they are | ||
| 273 | // relative to, this workaround should be removed. | ||
| 274 | const duped_source: LazyPath = switch (self.source) { | ||
| 275 | .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) }, | ||
| 276 | else => self.source.dupe(b), | ||
| 277 | }; | ||
| 278 | |||
| 279 | return .{ | ||
| 280 | .source = duped_source, | ||
| 281 | .dest_rel_path = b.dupePath(self.dest_rel_path), | ||
| 282 | }; | ||
| 283 | } | ||
| 284 | }; | ||
| 285 | |||
| 286 | pub const Directory = struct { | ||
| 287 | source: LazyPath, | ||
| 288 | dest_rel_path: []const u8, | ||
| 289 | options: Directory.Options, | ||
| 290 | |||
| 291 | pub const Options = struct { | ||
| 292 | /// File paths that end in any of these suffixes will be excluded from installation. | ||
| 293 | exclude_extensions: []const []const u8 = &.{}, | ||
| 294 | /// Only file paths that end in any of these suffixes will be included in installation. | ||
| 295 | /// `null` means that all suffixes will be included. | ||
| 296 | /// `exclude_extensions` takes precedence over `include_extensions`. | ||
| 297 | include_extensions: ?[]const []const u8 = &.{".h"}, | ||
| 298 | |||
| 299 | pub fn dupe(self: Directory.Options, b: *std.Build) Directory.Options { | ||
| 300 | return .{ | ||
| 301 | .exclude_extensions = b.dupeStrings(self.exclude_extensions), | ||
| 302 | .include_extensions = if (self.include_extensions) |incs| b.dupeStrings(incs) else null, | ||
| 303 | }; | ||
| 304 | } | ||
| 305 | }; | ||
| 306 | |||
| 307 | pub fn dupe(self: Directory, b: *std.Build) Directory { | ||
| 308 | // 'path' lazy paths are relative to the build root of some step, inferred from the step | ||
| 309 | // in which they are used. This means that we can't dupe such paths, because they may | ||
| 310 | // come from dependencies with their own build roots and duping the paths as is might | ||
| 311 | // cause the build script to search for the file relative to the wrong root. | ||
| 312 | // As a temporary workaround, we convert build root-relative paths to absolute paths. | ||
| 313 | // If/when the build-root relative paths are updated to encode which build root they are | ||
| 314 | // relative to, this workaround should be removed. | ||
| 315 | const duped_source: LazyPath = switch (self.source) { | ||
| 316 | .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) }, | ||
| 317 | else => self.source.dupe(b), | ||
| 318 | }; | ||
| 319 | |||
| 320 | return .{ | ||
| 321 | .source = duped_source, | ||
| 322 | .dest_rel_path = b.dupePath(self.dest_rel_path), | ||
| 323 | .options = self.options.dupe(b), | ||
| 324 | }; | ||
| 325 | } | ||
| 326 | }; | ||
| 327 | |||
| 328 | pub fn getSource(self: HeaderInstallation) LazyPath { | ||
| 329 | return switch (self) { | ||
| 330 | inline .file, .directory => |x| x.source, | ||
| 331 | }; | ||
| 332 | } | ||
| 333 | |||
| 334 | pub fn dupe(self: HeaderInstallation, b: *std.Build) HeaderInstallation { | ||
| 335 | return switch (self) { | ||
| 336 | .file => |f| .{ .file = f.dupe(b) }, | ||
| 337 | .directory => |d| .{ .directory = d.dupe(b) }, | ||
| 338 | }; | ||
| 339 | } | ||
| 340 | }; | ||
| 341 | |||
| 252 | pub fn create(owner: *std.Build, options: Options) *Compile { | 342 | pub fn create(owner: *std.Build, options: Options) *Compile { |
| 253 | const name = owner.dupe(options.name); | 343 | const name = owner.dupe(options.name); |
| 254 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { | 344 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { |
| ... | @@ -308,7 +398,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { | ... | @@ -308,7 +398,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { |
| 308 | .out_lib_filename = undefined, | 398 | .out_lib_filename = undefined, |
| 309 | .major_only_filename = null, | 399 | .major_only_filename = null, |
| 310 | .name_only_filename = null, | 400 | .name_only_filename = null, |
| 311 | .installed_headers = ArrayList(*Step).init(owner.allocator), | 401 | .installed_headers = ArrayList(HeaderInstallation).init(owner.allocator), |
| 312 | .zig_lib_dir = null, | 402 | .zig_lib_dir = null, |
| 313 | .exec_cmd_args = null, | 403 | .exec_cmd_args = null, |
| 314 | .filters = options.filters, | 404 | .filters = options.filters, |
| ... | @@ -380,78 +470,85 @@ pub fn create(owner: *std.Build, options: Options) *Compile { | ... | @@ -380,78 +470,85 @@ pub fn create(owner: *std.Build, options: Options) *Compile { |
| 380 | return self; | 470 | return self; |
| 381 | } | 471 | } |
| 382 | 472 | ||
| 383 | pub fn installHeader(cs: *Compile, src_path: []const u8, dest_rel_path: []const u8) void { | 473 | /// Marks the specified header for installation alongside this artifact. |
| 474 | /// When a module links with this artifact, all headers marked for installation are added to that | ||
| 475 | /// module's include search path. | ||
| 476 | pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) void { | ||
| 384 | const b = cs.step.owner; | 477 | const b = cs.step.owner; |
| 385 | const install_file = b.addInstallHeaderFile(src_path, dest_rel_path); | 478 | const installation: HeaderInstallation = .{ .file = .{ |
| 386 | b.getInstallStep().dependOn(&install_file.step); | 479 | .source = source.dupe(b), |
| 387 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | 480 | .dest_rel_path = b.dupePath(dest_rel_path), |
| 388 | } | 481 | } }; |
| 389 | 482 | cs.installed_headers.append(installation) catch @panic("OOM"); | |
| 390 | pub const InstallConfigHeaderOptions = struct { | 483 | cs.addHeaderInstallationToIncludeTree(installation); |
| 391 | install_dir: InstallDir = .header, | 484 | installation.getSource().addStepDependencies(&cs.step); |
| 392 | dest_rel_path: ?[]const u8 = null, | 485 | } |
| 393 | }; | 486 | |
| 394 | 487 | /// Marks headers from the specified directory for installation alongside this artifact. | |
| 395 | pub fn installConfigHeader( | 488 | /// When a module links with this artifact, all headers marked for installation are added to that |
| 396 | cs: *Compile, | 489 | /// module's include search path. |
| 397 | config_header: *Step.ConfigHeader, | ||
| 398 | options: InstallConfigHeaderOptions, | ||
| 399 | ) void { | ||
| 400 | const dest_rel_path = options.dest_rel_path orelse config_header.include_path; | ||
| 401 | const b = cs.step.owner; | ||
| 402 | const install_file = b.addInstallFileWithDir( | ||
| 403 | .{ .generated = &config_header.output_file }, | ||
| 404 | options.install_dir, | ||
| 405 | dest_rel_path, | ||
| 406 | ); | ||
| 407 | install_file.step.dependOn(&config_header.step); | ||
| 408 | b.getInstallStep().dependOn(&install_file.step); | ||
| 409 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | ||
| 410 | } | ||
| 411 | |||
| 412 | pub fn installHeadersDirectory( | 490 | pub fn installHeadersDirectory( |
| 413 | a: *Compile, | ||
| 414 | src_dir_path: []const u8, | ||
| 415 | dest_rel_path: []const u8, | ||
| 416 | ) void { | ||
| 417 | return installHeadersDirectoryOptions(a, .{ | ||
| 418 | .source_dir = .{ .path = src_dir_path }, | ||
| 419 | .install_dir = .header, | ||
| 420 | .install_subdir = dest_rel_path, | ||
| 421 | }); | ||
| 422 | } | ||
| 423 | |||
| 424 | pub fn installHeadersDirectoryOptions( | ||
| 425 | cs: *Compile, | 491 | cs: *Compile, |
| 426 | options: std.Build.Step.InstallDir.Options, | 492 | source: LazyPath, |
| 493 | dest_rel_path: []const u8, | ||
| 494 | options: HeaderInstallation.Directory.Options, | ||
| 427 | ) void { | 495 | ) void { |
| 428 | const b = cs.step.owner; | 496 | const b = cs.step.owner; |
| 429 | const install_dir = b.addInstallDirectory(options); | 497 | const installation: HeaderInstallation = .{ .directory = .{ |
| 430 | b.getInstallStep().dependOn(&install_dir.step); | 498 | .source = source.dupe(b), |
| 431 | cs.installed_headers.append(&install_dir.step) catch @panic("OOM"); | 499 | .dest_rel_path = b.dupePath(dest_rel_path), |
| 500 | .options = options.dupe(b), | ||
| 501 | } }; | ||
| 502 | cs.installed_headers.append(installation) catch @panic("OOM"); | ||
| 503 | cs.addHeaderInstallationToIncludeTree(installation); | ||
| 504 | installation.getSource().addStepDependencies(&cs.step); | ||
| 505 | } | ||
| 506 | |||
| 507 | /// Marks the specified config header for installation alongside this artifact. | ||
| 508 | /// When a module links with this artifact, all headers marked for installation are added to that | ||
| 509 | /// module's include search path. | ||
| 510 | pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void { | ||
| 511 | cs.installHeader(config_header.getOutput(), config_header.include_path); | ||
| 512 | } | ||
| 513 | |||
| 514 | /// Forwards all headers marked for installation from `lib` to this artifact. | ||
| 515 | /// When a module links with this artifact, all headers marked for installation are added to that | ||
| 516 | /// module's include search path. | ||
| 517 | pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void { | ||
| 518 | assert(lib.kind == .lib); | ||
| 519 | for (lib.installed_headers.items) |installation| { | ||
| 520 | const installation_copy = installation.dupe(lib.step.owner); | ||
| 521 | cs.installed_headers.append(installation_copy) catch @panic("OOM"); | ||
| 522 | cs.addHeaderInstallationToIncludeTree(installation_copy); | ||
| 523 | installation_copy.getSource().addStepDependencies(&cs.step); | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstallation) void { | ||
| 528 | if (cs.installed_headers_include_tree) |wf| switch (installation) { | ||
| 529 | .file => |file| { | ||
| 530 | _ = wf.addCopyFile(file.source, file.dest_rel_path); | ||
| 531 | }, | ||
| 532 | .directory => |dir| { | ||
| 533 | _ = wf.addCopyDirectory(dir.source, dir.dest_rel_path, .{ | ||
| 534 | .exclude_extensions = dir.options.exclude_extensions, | ||
| 535 | .include_extensions = dir.options.include_extensions, | ||
| 536 | }); | ||
| 537 | }, | ||
| 538 | }; | ||
| 432 | } | 539 | } |
| 433 | 540 | ||
| 434 | pub fn installLibraryHeaders(cs: *Compile, l: *Compile) void { | 541 | pub fn getEmittedIncludeTree(cs: *Compile) LazyPath { |
| 435 | assert(l.kind == .lib); | 542 | if (cs.installed_headers_include_tree) |wf| return wf.getDirectory(); |
| 436 | const b = cs.step.owner; | 543 | const b = cs.step.owner; |
| 437 | const install_step = b.getInstallStep(); | 544 | const wf = b.addWriteFiles(); |
| 438 | // Copy each element from installed_headers, modifying the builder | 545 | cs.installed_headers_include_tree = wf; |
| 439 | // to be the new parent's builder. | 546 | for (cs.installed_headers.items) |installation| { |
| 440 | for (l.installed_headers.items) |step| { | 547 | cs.addHeaderInstallationToIncludeTree(installation); |
| 441 | const step_copy = switch (step.id) { | 548 | } |
| 442 | inline .install_file, .install_dir => |id| blk: { | 549 | // The compile step itself does not need to depend on the write files step, |
| 443 | const T = id.Type(); | 550 | // only dependent modules do. |
| 444 | const ptr = b.allocator.create(T) catch @panic("OOM"); | 551 | return wf.getDirectory(); |
| 445 | ptr.* = step.cast(T).?.*; | ||
| 446 | ptr.dest_builder = b; | ||
| 447 | break :blk &ptr.step; | ||
| 448 | }, | ||
| 449 | else => unreachable, | ||
| 450 | }; | ||
| 451 | cs.installed_headers.append(step_copy) catch @panic("OOM"); | ||
| 452 | install_step.dependOn(step_copy); | ||
| 453 | } | ||
| 454 | cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); | ||
| 455 | } | 552 | } |
| 456 | 553 | ||
| 457 | pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy { | 554 | pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy { |
lib/std/Build/Step/InstallArtifact.zig+74-21| ... | @@ -77,12 +77,10 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins | ... | @@ -77,12 +77,10 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins |
| 77 | }, | 77 | }, |
| 78 | .h_dir = switch (options.h_dir) { | 78 | .h_dir = switch (options.h_dir) { |
| 79 | .disabled => null, | 79 | .disabled => null, |
| 80 | // https://github.com/ziglang/zig/issues/9698 | 80 | .default => switch (artifact.kind) { |
| 81 | .default => null, | 81 | .lib => .header, |
| 82 | //.default => switch (artifact.kind) { | 82 | else => null, |
| 83 | // .lib => .header, | 83 | }, |
| 84 | // else => null, | ||
| 85 | //}, | ||
| 86 | .override => |o| o, | 84 | .override => |o| o, |
| 87 | }, | 85 | }, |
| 88 | .implib_dir = switch (options.implib_dir) { | 86 | .implib_dir = switch (options.implib_dir) { |
| ... | @@ -113,7 +111,8 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins | ... | @@ -113,7 +111,8 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins |
| 113 | 111 | ||
| 114 | if (self.dest_dir != null) self.emitted_bin = artifact.getEmittedBin(); | 112 | if (self.dest_dir != null) self.emitted_bin = artifact.getEmittedBin(); |
| 115 | if (self.pdb_dir != null) self.emitted_pdb = artifact.getEmittedPdb(); | 113 | if (self.pdb_dir != null) self.emitted_pdb = artifact.getEmittedPdb(); |
| 116 | if (self.h_dir != null) self.emitted_h = artifact.getEmittedH(); | 114 | // https://github.com/ziglang/zig/issues/9698 |
| 115 | //if (self.h_dir != null) self.emitted_h = artifact.getEmittedH(); | ||
| 117 | if (self.implib_dir != null) self.emitted_implib = artifact.getEmittedImplib(); | 116 | if (self.implib_dir != null) self.emitted_implib = artifact.getEmittedImplib(); |
| 118 | 117 | ||
| 119 | return self; | 118 | return self; |
| ... | @@ -122,14 +121,14 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins | ... | @@ -122,14 +121,14 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins |
| 122 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { | 121 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 123 | _ = prog_node; | 122 | _ = prog_node; |
| 124 | const self: *InstallArtifact = @fieldParentPtr("step", step); | 123 | const self: *InstallArtifact = @fieldParentPtr("step", step); |
| 125 | const dest_builder = step.owner; | 124 | const b = step.owner; |
| 126 | const cwd = fs.cwd(); | 125 | const cwd = fs.cwd(); |
| 127 | 126 | ||
| 128 | var all_cached = true; | 127 | var all_cached = true; |
| 129 | 128 | ||
| 130 | if (self.dest_dir) |dest_dir| { | 129 | if (self.dest_dir) |dest_dir| { |
| 131 | const full_dest_path = dest_builder.getInstallPath(dest_dir, self.dest_sub_path); | 130 | const full_dest_path = b.getInstallPath(dest_dir, self.dest_sub_path); |
| 132 | const full_src_path = self.emitted_bin.?.getPath2(step.owner, step); | 131 | const full_src_path = self.emitted_bin.?.getPath2(b, step); |
| 133 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { | 132 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { |
| 134 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | 133 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ |
| 135 | full_src_path, full_dest_path, @errorName(err), | 134 | full_src_path, full_dest_path, @errorName(err), |
| ... | @@ -145,8 +144,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -145,8 +144,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 145 | } | 144 | } |
| 146 | 145 | ||
| 147 | if (self.implib_dir) |implib_dir| { | 146 | if (self.implib_dir) |implib_dir| { |
| 148 | const full_src_path = self.emitted_implib.?.getPath2(step.owner, step); | 147 | const full_src_path = self.emitted_implib.?.getPath2(b, step); |
| 149 | const full_implib_path = dest_builder.getInstallPath(implib_dir, fs.path.basename(full_src_path)); | 148 | const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(full_src_path)); |
| 150 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { | 149 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { |
| 151 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | 150 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ |
| 152 | full_src_path, full_implib_path, @errorName(err), | 151 | full_src_path, full_implib_path, @errorName(err), |
| ... | @@ -156,8 +155,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -156,8 +155,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 156 | } | 155 | } |
| 157 | 156 | ||
| 158 | if (self.pdb_dir) |pdb_dir| { | 157 | if (self.pdb_dir) |pdb_dir| { |
| 159 | const full_src_path = self.emitted_pdb.?.getPath2(step.owner, step); | 158 | const full_src_path = self.emitted_pdb.?.getPath2(b, step); |
| 160 | const full_pdb_path = dest_builder.getInstallPath(pdb_dir, fs.path.basename(full_src_path)); | 159 | const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(full_src_path)); |
| 161 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { | 160 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { |
| 162 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | 161 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ |
| 163 | full_src_path, full_pdb_path, @errorName(err), | 162 | full_src_path, full_pdb_path, @errorName(err), |
| ... | @@ -167,14 +166,68 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -167,14 +166,68 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 167 | } | 166 | } |
| 168 | 167 | ||
| 169 | if (self.h_dir) |h_dir| { | 168 | if (self.h_dir) |h_dir| { |
| 170 | const full_src_path = self.emitted_h.?.getPath2(step.owner, step); | 169 | if (self.emitted_h) |emitted_h| { |
| 171 | const full_h_path = dest_builder.getInstallPath(h_dir, fs.path.basename(full_src_path)); | 170 | const full_src_path = emitted_h.getPath2(b, step); |
| 172 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { | 171 | const full_h_path = b.getInstallPath(h_dir, fs.path.basename(full_src_path)); |
| 173 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | 172 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { |
| 174 | full_src_path, full_h_path, @errorName(err), | 173 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ |
| 175 | }); | 174 | full_src_path, full_h_path, @errorName(err), |
| 175 | }); | ||
| 176 | }; | ||
| 177 | all_cached = all_cached and p == .fresh; | ||
| 178 | } | ||
| 179 | |||
| 180 | for (self.artifact.installed_headers.items) |installation| switch (installation) { | ||
| 181 | .file => |file| { | ||
| 182 | const full_src_path = file.source.getPath2(b, step); | ||
| 183 | const full_h_path = b.getInstallPath(h_dir, file.dest_rel_path); | ||
| 184 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { | ||
| 185 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 186 | full_src_path, full_h_path, @errorName(err), | ||
| 187 | }); | ||
| 188 | }; | ||
| 189 | all_cached = all_cached and p == .fresh; | ||
| 190 | }, | ||
| 191 | .directory => |dir| { | ||
| 192 | const full_src_dir_path = dir.source.getPath2(b, step); | ||
| 193 | const full_h_prefix = b.getInstallPath(h_dir, dir.dest_rel_path); | ||
| 194 | |||
| 195 | var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| { | ||
| 196 | return step.fail("unable to open source directory '{s}': {s}", .{ | ||
| 197 | full_src_dir_path, @errorName(err), | ||
| 198 | }); | ||
| 199 | }; | ||
| 200 | defer src_dir.close(); | ||
| 201 | |||
| 202 | var it = try src_dir.walk(b.allocator); | ||
| 203 | next_entry: while (try it.next()) |entry| { | ||
| 204 | for (dir.options.exclude_extensions) |ext| { | ||
| 205 | if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry; | ||
| 206 | } | ||
| 207 | if (dir.options.include_extensions) |incs| { | ||
| 208 | for (incs) |inc| { | ||
| 209 | if (std.mem.endsWith(u8, entry.path, inc)) break; | ||
| 210 | } else { | ||
| 211 | continue :next_entry; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path }); | ||
| 215 | const full_dest_path = b.pathJoin(&.{ full_h_prefix, entry.path }); | ||
| 216 | switch (entry.kind) { | ||
| 217 | .directory => try cwd.makePath(full_dest_path), | ||
| 218 | .file => { | ||
| 219 | const p = fs.Dir.updateFile(cwd, full_src_entry_path, cwd, full_dest_path, .{}) catch |err| { | ||
| 220 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 221 | full_src_entry_path, full_dest_path, @errorName(err), | ||
| 222 | }); | ||
| 223 | }; | ||
| 224 | all_cached = all_cached and p == .fresh; | ||
| 225 | }, | ||
| 226 | else => continue, | ||
| 227 | } | ||
| 228 | } | ||
| 229 | }, | ||
| 176 | }; | 230 | }; |
| 177 | all_cached = all_cached and p == .fresh; | ||
| 178 | } | 231 | } |
| 179 | 232 | ||
| 180 | step.result_cached = all_cached; | 233 | step.result_cached = all_cached; |
lib/std/Build/Step/InstallDir.zig+9-14| ... | @@ -8,9 +8,6 @@ const InstallDirStep = @This(); | ... | @@ -8,9 +8,6 @@ const InstallDirStep = @This(); |
| 8 | 8 | ||
| 9 | step: Step, | 9 | step: Step, |
| 10 | options: Options, | 10 | options: Options, |
| 11 | /// This is used by the build system when a file being installed comes from one | ||
| 12 | /// package but is being installed by another. | ||
| 13 | dest_builder: *std.Build, | ||
| 14 | 11 | ||
| 15 | pub const base_id = .install_dir; | 12 | pub const base_id = .install_dir; |
| 16 | 13 | ||
| ... | @@ -55,7 +52,6 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep { | ... | @@ -55,7 +52,6 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep { |
| 55 | .makeFn = make, | 52 | .makeFn = make, |
| 56 | }), | 53 | }), |
| 57 | .options = options.dupe(owner), | 54 | .options = options.dupe(owner), |
| 58 | .dest_builder = owner, | ||
| 59 | }; | 55 | }; |
| 60 | options.source_dir.addStepDependencies(&self.step); | 56 | options.source_dir.addStepDependencies(&self.step); |
| 61 | return self; | 57 | return self; |
| ... | @@ -63,15 +59,14 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep { | ... | @@ -63,15 +59,14 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep { |
| 63 | 59 | ||
| 64 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { | 60 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 65 | _ = prog_node; | 61 | _ = prog_node; |
| 62 | const b = step.owner; | ||
| 66 | const self: *InstallDirStep = @fieldParentPtr("step", step); | 63 | const self: *InstallDirStep = @fieldParentPtr("step", step); |
| 67 | const dest_builder = self.dest_builder; | 64 | const arena = b.allocator; |
| 68 | const arena = dest_builder.allocator; | 65 | const dest_prefix = b.getInstallPath(self.options.install_dir, self.options.install_subdir); |
| 69 | const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir); | 66 | const src_dir_path = self.options.source_dir.getPath2(b, step); |
| 70 | const src_builder = self.step.owner; | 67 | var src_dir = b.build_root.handle.openDir(src_dir_path, .{ .iterate = true }) catch |err| { |
| 71 | const src_dir_path = self.options.source_dir.getPath2(src_builder, step); | ||
| 72 | var src_dir = src_builder.build_root.handle.openDir(src_dir_path, .{ .iterate = true }) catch |err| { | ||
| 73 | return step.fail("unable to open source directory '{}{s}': {s}", .{ | 68 | return step.fail("unable to open source directory '{}{s}': {s}", .{ |
| 74 | src_builder.build_root, src_dir_path, @errorName(err), | 69 | b.build_root, src_dir_path, @errorName(err), |
| 75 | }); | 70 | }); |
| 76 | }; | 71 | }; |
| 77 | defer src_dir.close(); | 72 | defer src_dir.close(); |
| ... | @@ -104,20 +99,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -104,20 +99,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 104 | .file => { | 99 | .file => { |
| 105 | for (self.options.blank_extensions) |ext| { | 100 | for (self.options.blank_extensions) |ext| { |
| 106 | if (mem.endsWith(u8, entry.path, ext)) { | 101 | if (mem.endsWith(u8, entry.path, ext)) { |
| 107 | try dest_builder.truncateFile(dest_path); | 102 | try b.truncateFile(dest_path); |
| 108 | continue :next_entry; | 103 | continue :next_entry; |
| 109 | } | 104 | } |
| 110 | } | 105 | } |
| 111 | 106 | ||
| 112 | const prev_status = fs.Dir.updateFile( | 107 | const prev_status = fs.Dir.updateFile( |
| 113 | src_builder.build_root.handle, | 108 | b.build_root.handle, |
| 114 | src_sub_path, | 109 | src_sub_path, |
| 115 | cwd, | 110 | cwd, |
| 116 | dest_path, | 111 | dest_path, |
| 117 | .{}, | 112 | .{}, |
| 118 | ) catch |err| { | 113 | ) catch |err| { |
| 119 | return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{ | 114 | return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{ |
| 120 | src_builder.build_root, src_sub_path, dest_path, @errorName(err), | 115 | b.build_root, src_sub_path, dest_path, @errorName(err), |
| 121 | }); | 116 | }); |
| 122 | }; | 117 | }; |
| 123 | all_cached = all_cached and prev_status == .fresh; | 118 | all_cached = all_cached and prev_status == .fresh; |
lib/std/Build/Step/InstallFile.zig+3-8| ... | @@ -11,9 +11,6 @@ step: Step, | ... | @@ -11,9 +11,6 @@ step: Step, |
| 11 | source: LazyPath, | 11 | source: LazyPath, |
| 12 | dir: InstallDir, | 12 | dir: InstallDir, |
| 13 | dest_rel_path: []const u8, | 13 | dest_rel_path: []const u8, |
| 14 | /// This is used by the build system when a file being installed comes from one | ||
| 15 | /// package but is being installed by another. | ||
| 16 | dest_builder: *std.Build, | ||
| 17 | 14 | ||
| 18 | pub fn create( | 15 | pub fn create( |
| 19 | owner: *std.Build, | 16 | owner: *std.Build, |
| ... | @@ -34,7 +31,6 @@ pub fn create( | ... | @@ -34,7 +31,6 @@ pub fn create( |
| 34 | .source = source.dupe(owner), | 31 | .source = source.dupe(owner), |
| 35 | .dir = dir.dupe(owner), | 32 | .dir = dir.dupe(owner), |
| 36 | .dest_rel_path = owner.dupePath(dest_rel_path), | 33 | .dest_rel_path = owner.dupePath(dest_rel_path), |
| 37 | .dest_builder = owner, | ||
| 38 | }; | 34 | }; |
| 39 | source.addStepDependencies(&self.step); | 35 | source.addStepDependencies(&self.step); |
| 40 | return self; | 36 | return self; |
| ... | @@ -42,11 +38,10 @@ pub fn create( | ... | @@ -42,11 +38,10 @@ pub fn create( |
| 42 | 38 | ||
| 43 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { | 39 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 44 | _ = prog_node; | 40 | _ = prog_node; |
| 45 | const src_builder = step.owner; | 41 | const b = step.owner; |
| 46 | const self: *InstallFile = @fieldParentPtr("step", step); | 42 | const self: *InstallFile = @fieldParentPtr("step", step); |
| 47 | const dest_builder = self.dest_builder; | 43 | const full_src_path = self.source.getPath2(b, step); |
| 48 | const full_src_path = self.source.getPath2(src_builder, step); | 44 | const full_dest_path = b.getInstallPath(self.dir, self.dest_rel_path); |
| 49 | const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path); | ||
| 50 | const cwd = std.fs.cwd(); | 45 | const cwd = std.fs.cwd(); |
| 51 | const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { | 46 | const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { |
| 52 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | 47 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ |
lib/std/Build/Step/WriteFile.zig+130-4| ... | @@ -15,9 +15,11 @@ const ArrayList = std.ArrayList; | ... | @@ -15,9 +15,11 @@ const ArrayList = std.ArrayList; |
| 15 | const WriteFile = @This(); | 15 | const WriteFile = @This(); |
| 16 | 16 | ||
| 17 | step: Step, | 17 | step: Step, |
| 18 | /// The elements here are pointers because we need stable pointers for the | 18 | |
| 19 | /// GeneratedFile field. | 19 | // The elements here are pointers because we need stable pointers for the GeneratedFile field. |
| 20 | files: std.ArrayListUnmanaged(*File), | 20 | files: std.ArrayListUnmanaged(*File), |
| 21 | directories: std.ArrayListUnmanaged(*Directory), | ||
| 22 | |||
| 21 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), | 23 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), |
| 22 | generated_directory: std.Build.GeneratedFile, | 24 | generated_directory: std.Build.GeneratedFile, |
| 23 | 25 | ||
| ... | @@ -33,6 +35,33 @@ pub const File = struct { | ... | @@ -33,6 +35,33 @@ pub const File = struct { |
| 33 | } | 35 | } |
| 34 | }; | 36 | }; |
| 35 | 37 | ||
| 38 | pub const Directory = struct { | ||
| 39 | source: std.Build.LazyPath, | ||
| 40 | sub_path: []const u8, | ||
| 41 | options: Options, | ||
| 42 | generated_dir: std.Build.GeneratedFile, | ||
| 43 | |||
| 44 | pub const Options = struct { | ||
| 45 | /// File paths that end in any of these suffixes will be excluded from copying. | ||
| 46 | exclude_extensions: []const []const u8 = &.{}, | ||
| 47 | /// Only file paths that end in any of these suffixes will be included in copying. | ||
| 48 | /// `null` means that all suffixes will be included. | ||
| 49 | /// `exclude_extensions` takes precedence over `include_extensions`. | ||
| 50 | include_extensions: ?[]const []const u8 = null, | ||
| 51 | |||
| 52 | pub fn dupe(self: Options, b: *std.Build) Options { | ||
| 53 | return .{ | ||
| 54 | .exclude_extensions = b.dupeStrings(self.exclude_extensions), | ||
| 55 | .include_extensions = if (self.include_extensions) |incs| b.dupeStrings(incs) else null, | ||
| 56 | }; | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | pub fn getPath(self: *Directory) std.Build.LazyPath { | ||
| 61 | return .{ .generated = &self.generated_dir }; | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 36 | pub const OutputSourceFile = struct { | 65 | pub const OutputSourceFile = struct { |
| 37 | contents: Contents, | 66 | contents: Contents, |
| 38 | sub_path: []const u8, | 67 | sub_path: []const u8, |
| ... | @@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile { | ... | @@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile { |
| 53 | .makeFn = make, | 82 | .makeFn = make, |
| 54 | }), | 83 | }), |
| 55 | .files = .{}, | 84 | .files = .{}, |
| 85 | .directories = .{}, | ||
| 56 | .output_source_files = .{}, | 86 | .output_source_files = .{}, |
| 57 | .generated_directory = .{ .step = &wf.step }, | 87 | .generated_directory = .{ .step = &wf.step }, |
| 58 | }; | 88 | }; |
| ... | @@ -96,6 +126,31 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const | ... | @@ -96,6 +126,31 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const |
| 96 | return file.getPath(); | 126 | return file.getPath(); |
| 97 | } | 127 | } |
| 98 | 128 | ||
| 129 | /// Copy files matching the specified exclude/include patterns to the specified subdirectory | ||
| 130 | /// relative to this step's generated directory. | ||
| 131 | /// The returned value is a lazy path to the generated subdirectory. | ||
| 132 | pub fn addCopyDirectory( | ||
| 133 | wf: *WriteFile, | ||
| 134 | source: std.Build.LazyPath, | ||
| 135 | sub_path: []const u8, | ||
| 136 | options: Directory.Options, | ||
| 137 | ) std.Build.LazyPath { | ||
| 138 | const b = wf.step.owner; | ||
| 139 | const gpa = b.allocator; | ||
| 140 | const dir = gpa.create(Directory) catch @panic("OOM"); | ||
| 141 | dir.* = .{ | ||
| 142 | .source = source.dupe(b), | ||
| 143 | .sub_path = b.dupePath(sub_path), | ||
| 144 | .options = options.dupe(b), | ||
| 145 | .generated_dir = .{ .step = &wf.step }, | ||
| 146 | }; | ||
| 147 | wf.directories.append(gpa, dir) catch @panic("OOM"); | ||
| 148 | |||
| 149 | wf.maybeUpdateName(); | ||
| 150 | source.addStepDependencies(&wf.step); | ||
| 151 | return dir.getPath(); | ||
| 152 | } | ||
| 153 | |||
| 99 | /// A path relative to the package root. | 154 | /// A path relative to the package root. |
| 100 | /// Be careful with this because it updates source files. This should not be | 155 | /// Be careful with this because it updates source files. This should not be |
| 101 | /// used as part of the normal build process, but as a utility occasionally | 156 | /// used as part of the normal build process, but as a utility occasionally |
| ... | @@ -130,11 +185,16 @@ pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath { | ... | @@ -130,11 +185,16 @@ pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath { |
| 130 | } | 185 | } |
| 131 | 186 | ||
| 132 | fn maybeUpdateName(wf: *WriteFile) void { | 187 | fn maybeUpdateName(wf: *WriteFile) void { |
| 133 | if (wf.files.items.len == 1) { | 188 | if (wf.files.items.len == 1 and wf.directories.items.len == 0) { |
| 134 | // First time adding a file; update name. | 189 | // First time adding a file; update name. |
| 135 | if (std.mem.eql(u8, wf.step.name, "WriteFile")) { | 190 | if (std.mem.eql(u8, wf.step.name, "WriteFile")) { |
| 136 | wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path}); | 191 | wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path}); |
| 137 | } | 192 | } |
| 193 | } else if (wf.directories.items.len == 1 and wf.files.items.len == 0) { | ||
| 194 | // First time adding a directory; update name. | ||
| 195 | if (std.mem.eql(u8, wf.step.name, "WriteFile")) { | ||
| 196 | wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.directories.items[0].sub_path}); | ||
| 197 | } | ||
| 138 | } | 198 | } |
| 139 | } | 199 | } |
| 140 | 200 | ||
| ... | @@ -209,6 +269,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -209,6 +269,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 209 | }, | 269 | }, |
| 210 | } | 270 | } |
| 211 | } | 271 | } |
| 272 | for (wf.directories.items) |dir| { | ||
| 273 | man.hash.addBytes(dir.source.getPath2(b, step)); | ||
| 274 | man.hash.addBytes(dir.sub_path); | ||
| 275 | for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext); | ||
| 276 | if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc); | ||
| 277 | } | ||
| 212 | 278 | ||
| 213 | if (try step.cacheHit(&man)) { | 279 | if (try step.cacheHit(&man)) { |
| 214 | const digest = man.final(); | 280 | const digest = man.final(); |
| ... | @@ -233,6 +299,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -233,6 +299,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 233 | }; | 299 | }; |
| 234 | defer cache_dir.close(); | 300 | defer cache_dir.close(); |
| 235 | 301 | ||
| 302 | const cwd = fs.cwd(); | ||
| 303 | |||
| 236 | for (wf.files.items) |file| { | 304 | for (wf.files.items) |file| { |
| 237 | if (fs.path.dirname(file.sub_path)) |dirname| { | 305 | if (fs.path.dirname(file.sub_path)) |dirname| { |
| 238 | cache_dir.makePath(dirname) catch |err| { | 306 | cache_dir.makePath(dirname) catch |err| { |
| ... | @@ -252,7 +320,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -252,7 +320,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 252 | .copy => |file_source| { | 320 | .copy => |file_source| { |
| 253 | const source_path = file_source.getPath(b); | 321 | const source_path = file_source.getPath(b); |
| 254 | const prev_status = fs.Dir.updateFile( | 322 | const prev_status = fs.Dir.updateFile( |
| 255 | fs.cwd(), | 323 | cwd, |
| 256 | source_path, | 324 | source_path, |
| 257 | cache_dir, | 325 | cache_dir, |
| 258 | file.sub_path, | 326 | file.sub_path, |
| ... | @@ -279,6 +347,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -279,6 +347,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 279 | cache_path, file.sub_path, | 347 | cache_path, file.sub_path, |
| 280 | }); | 348 | }); |
| 281 | } | 349 | } |
| 350 | for (wf.directories.items) |dir| { | ||
| 351 | const full_src_dir_path = dir.source.getPath2(b, step); | ||
| 352 | const dest_dirname = dir.sub_path; | ||
| 353 | |||
| 354 | if (dest_dirname.len != 0) { | ||
| 355 | cache_dir.makePath(dest_dirname) catch |err| { | ||
| 356 | return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ | ||
| 357 | b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err), | ||
| 358 | }); | ||
| 359 | }; | ||
| 360 | } | ||
| 361 | |||
| 362 | var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| { | ||
| 363 | return step.fail("unable to open source directory '{s}': {s}", .{ | ||
| 364 | full_src_dir_path, @errorName(err), | ||
| 365 | }); | ||
| 366 | }; | ||
| 367 | defer src_dir.close(); | ||
| 368 | |||
| 369 | var it = try src_dir.walk(b.allocator); | ||
| 370 | next_entry: while (try it.next()) |entry| { | ||
| 371 | for (dir.options.exclude_extensions) |ext| { | ||
| 372 | if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry; | ||
| 373 | } | ||
| 374 | if (dir.options.include_extensions) |incs| { | ||
| 375 | for (incs) |inc| { | ||
| 376 | if (std.mem.endsWith(u8, entry.path, inc)) break; | ||
| 377 | } else { | ||
| 378 | continue :next_entry; | ||
| 379 | } | ||
| 380 | } | ||
| 381 | const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path }); | ||
| 382 | const dest_path = b.pathJoin(&.{ dest_dirname, entry.path }); | ||
| 383 | switch (entry.kind) { | ||
| 384 | .directory => try cache_dir.makePath(dest_path), | ||
| 385 | .file => { | ||
| 386 | const prev_status = fs.Dir.updateFile( | ||
| 387 | cwd, | ||
| 388 | full_src_entry_path, | ||
| 389 | cache_dir, | ||
| 390 | dest_path, | ||
| 391 | .{}, | ||
| 392 | ) catch |err| { | ||
| 393 | return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{ | ||
| 394 | full_src_entry_path, | ||
| 395 | b.cache_root, | ||
| 396 | cache_path, | ||
| 397 | fs.path.sep, | ||
| 398 | dest_path, | ||
| 399 | @errorName(err), | ||
| 400 | }); | ||
| 401 | }; | ||
| 402 | _ = prev_status; | ||
| 403 | }, | ||
| 404 | else => continue, | ||
| 405 | } | ||
| 406 | } | ||
| 407 | } | ||
| 282 | 408 | ||
| 283 | try step.writeManifest(&man); | 409 | try step.writeManifest(&man); |
| 284 | } | 410 | } |
test/standalone.zig+4| ... | @@ -266,6 +266,10 @@ pub const build_cases = [_]BuildCase{ | ... | @@ -266,6 +266,10 @@ pub const build_cases = [_]BuildCase{ |
| 266 | .build_root = "test/standalone/depend_on_main_mod", | 266 | .build_root = "test/standalone/depend_on_main_mod", |
| 267 | .import = @import("standalone/depend_on_main_mod/build.zig"), | 267 | .import = @import("standalone/depend_on_main_mod/build.zig"), |
| 268 | }, | 268 | }, |
| 269 | .{ | ||
| 270 | .build_root = "test/standalone/install_headers", | ||
| 271 | .import = @import("standalone/install_headers/build.zig"), | ||
| 272 | }, | ||
| 269 | }; | 273 | }; |
| 270 | 274 | ||
| 271 | const std = @import("std"); | 275 | const std = @import("std"); |
test/standalone/install_headers/build.zig created+99| ... | @@ -0,0 +1,99 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const test_step = b.step("test", "Test"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const empty_c = b.addWriteFiles().add("empty.c", ""); | ||
| 8 | |||
| 9 | const libfoo = b.addStaticLibrary(.{ | ||
| 10 | .name = "foo", | ||
| 11 | .target = b.resolveTargetQuery(.{}), | ||
| 12 | .optimize = .Debug, | ||
| 13 | }); | ||
| 14 | libfoo.addCSourceFile(.{ .file = empty_c }); | ||
| 15 | |||
| 16 | const exe = b.addExecutable(.{ | ||
| 17 | .name = "exe", | ||
| 18 | .target = b.resolveTargetQuery(.{}), | ||
| 19 | .optimize = .Debug, | ||
| 20 | .link_libc = true, | ||
| 21 | }); | ||
| 22 | exe.addCSourceFile(.{ .file = b.addWriteFiles().add("main.c", | ||
| 23 | \\#include <stdio.h> | ||
| 24 | \\#include <foo/a.h> | ||
| 25 | \\#include <foo/sub_dir/b.h> | ||
| 26 | \\#include <foo/d.h> | ||
| 27 | \\#include <foo/config.h> | ||
| 28 | \\#include <bar.h> | ||
| 29 | \\int main(void) { | ||
| 30 | \\ printf(FOO_A FOO_B FOO_D FOO_CONFIG_1 FOO_CONFIG_2 BAR_X); | ||
| 31 | \\ return 0; | ||
| 32 | \\} | ||
| 33 | ) }); | ||
| 34 | |||
| 35 | libfoo.installHeadersDirectory(.{ .path = "include" }, "foo", .{ .exclude_extensions = &.{".ignore_me.h"} }); | ||
| 36 | libfoo.installHeader(b.addWriteFiles().add("d.h", | ||
| 37 | \\#define FOO_D "D" | ||
| 38 | \\ | ||
| 39 | ), "foo/d.h"); | ||
| 40 | |||
| 41 | if (libfoo.installed_headers_include_tree != null) std.debug.panic("include tree step was created before linking", .{}); | ||
| 42 | |||
| 43 | // Link before we have registered all headers for installation, | ||
| 44 | // to verify that the lazily created write files step is properly taken into account. | ||
| 45 | exe.linkLibrary(libfoo); | ||
| 46 | |||
| 47 | if (libfoo.installed_headers_include_tree == null) std.debug.panic("include tree step was not created after linking", .{}); | ||
| 48 | |||
| 49 | libfoo.installConfigHeader(b.addConfigHeader(.{ | ||
| 50 | .style = .blank, | ||
| 51 | .include_path = "foo/config.h", | ||
| 52 | }, .{ | ||
| 53 | .FOO_CONFIG_1 = "1", | ||
| 54 | .FOO_CONFIG_2 = "2", | ||
| 55 | })); | ||
| 56 | |||
| 57 | const libbar = b.addStaticLibrary(.{ | ||
| 58 | .name = "bar", | ||
| 59 | .target = b.resolveTargetQuery(.{}), | ||
| 60 | .optimize = .Debug, | ||
| 61 | }); | ||
| 62 | libbar.addCSourceFile(.{ .file = empty_c }); | ||
| 63 | libbar.installHeader(b.addWriteFiles().add("bar.h", | ||
| 64 | \\#define BAR_X "X" | ||
| 65 | \\ | ||
| 66 | ), "bar.h"); | ||
| 67 | libfoo.installLibraryHeaders(libbar); | ||
| 68 | |||
| 69 | const run_exe = b.addRunArtifact(exe); | ||
| 70 | run_exe.expectStdOutEqual("ABD12X"); | ||
| 71 | test_step.dependOn(&run_exe.step); | ||
| 72 | |||
| 73 | const install_libfoo = b.addInstallArtifact(libfoo, .{ | ||
| 74 | .dest_dir = .{ .override = .{ .custom = "custom" } }, | ||
| 75 | .h_dir = .{ .override = .{ .custom = "custom/include" } }, | ||
| 76 | .implib_dir = .disabled, | ||
| 77 | .pdb_dir = .disabled, | ||
| 78 | }); | ||
| 79 | const check_exists = b.addExecutable(.{ | ||
| 80 | .name = "check_exists", | ||
| 81 | .root_source_file = .{ .path = "check_exists.zig" }, | ||
| 82 | .target = b.resolveTargetQuery(.{}), | ||
| 83 | .optimize = .Debug, | ||
| 84 | }); | ||
| 85 | const run_check_exists = b.addRunArtifact(check_exists); | ||
| 86 | run_check_exists.addArgs(&.{ | ||
| 87 | "custom/include/foo/a.h", | ||
| 88 | "!custom/include/foo/ignore_me.txt", | ||
| 89 | "custom/include/foo/sub_dir/b.h", | ||
| 90 | "!custom/include/foo/sub_dir/c.ignore_me.h", | ||
| 91 | "custom/include/foo/d.h", | ||
| 92 | "custom/include/foo/config.h", | ||
| 93 | "custom/include/bar.h", | ||
| 94 | }); | ||
| 95 | run_check_exists.setCwd(.{ .cwd_relative = b.getInstallPath(.prefix, "") }); | ||
| 96 | run_check_exists.expectExitCode(0); | ||
| 97 | run_check_exists.step.dependOn(&install_libfoo.step); | ||
| 98 | test_step.dependOn(&run_check_exists.step); | ||
| 99 | } | ||
test/standalone/install_headers/check_exists.zig created+37| ... | @@ -0,0 +1,37 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | /// Checks the existence of files relative to cwd. | ||
| 4 | /// A path starting with ! should not exist. | ||
| 5 | pub fn main() !void { | ||
| 6 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | ||
| 7 | defer arena_state.deinit(); | ||
| 8 | |||
| 9 | const arena = arena_state.allocator(); | ||
| 10 | |||
| 11 | var arg_it = try std.process.argsWithAllocator(arena); | ||
| 12 | _ = arg_it.next(); | ||
| 13 | |||
| 14 | const cwd = std.fs.cwd(); | ||
| 15 | const cwd_realpath = try cwd.realpathAlloc(arena, "."); | ||
| 16 | |||
| 17 | while (arg_it.next()) |file_path| { | ||
| 18 | if (file_path.len > 0 and file_path[0] == '!') { | ||
| 19 | errdefer std.log.err( | ||
| 20 | "exclusive file check '{s}{c}{s}' failed", | ||
| 21 | .{ cwd_realpath, std.fs.path.sep, file_path[1..] }, | ||
| 22 | ); | ||
| 23 | if (std.fs.cwd().statFile(file_path[1..])) |_| { | ||
| 24 | return error.FileFound; | ||
| 25 | } else |err| switch (err) { | ||
| 26 | error.FileNotFound => {}, | ||
| 27 | else => return err, | ||
| 28 | } | ||
| 29 | } else { | ||
| 30 | errdefer std.log.err( | ||
| 31 | "inclusive file check '{s}{c}{s}' failed", | ||
| 32 | .{ cwd_realpath, std.fs.path.sep, file_path }, | ||
| 33 | ); | ||
| 34 | _ = try std.fs.cwd().statFile(file_path); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
test/standalone/install_headers/include/a.h created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | #define FOO_A "A" | ||
test/standalone/install_headers/include/ignore_me.txt created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | ignore me | ||
test/standalone/install_headers/include/sub_dir/b.h created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | #define FOO_B "B" | ||
test/standalone/install_headers/include/sub_dir/c.ignore_me.h created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | #error "ignore me" | ||