| 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 | 1568 | return Step.ObjCopy.create(b, source, options); |
| 1569 | 1569 | } |
| 1570 | 1570 | |
| 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 { | |
| 1573 | return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path); | |
| 1571 | /// `dest_rel_path` is relative to install prefix path | |
| 1572 | pub fn addInstallFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { | |
| 1573 | return b.addInstallFileWithDir(source, .prefix, dest_rel_path); | |
| 1574 | 1574 | } |
| 1575 | 1575 | |
| 1576 | ///`dest_rel_path` is relative to bin path | |
| 1577 | pub fn addInstallBinFile(self: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { | |
| 1578 | return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path); | |
| 1576 | /// `dest_rel_path` is relative to bin path | |
| 1577 | pub fn addInstallBinFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { | |
| 1578 | return b.addInstallFileWithDir(source, .bin, dest_rel_path); | |
| 1579 | 1579 | } |
| 1580 | 1580 | |
| 1581 | ///`dest_rel_path` is relative to lib path | |
| 1582 | pub fn addInstallLibFile(self: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { | |
| 1583 | return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path); | |
| 1581 | /// `dest_rel_path` is relative to lib path | |
| 1582 | pub fn addInstallLibFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { | |
| 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 { | |
| 1587 | return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); | |
| 1586 | /// `dest_rel_path` is relative to header 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 | 1591 | pub fn addInstallFileWithDir( |
| ... | ... | @@ -1593,7 +1594,7 @@ pub fn addInstallFileWithDir( |
| 1593 | 1594 | install_dir: InstallDir, |
| 1594 | 1595 | dest_rel_path: []const u8, |
| 1595 | 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 | 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 | 265 | for (dependee.link_objects.items) |link_object| switch (link_object) { |
| 266 | 266 | .other_step => |compile| { |
| 267 | 267 | addStepDependencies(m, dependee, &compile.step); |
| 268 | for (compile.installed_headers.items) |install_step| | |
| 269 | addStepDependenciesOnly(m, install_step); | |
| 268 | addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree()); | |
| 270 | 269 | }, |
| 271 | 270 | |
| 272 | 271 | .static_path, |
| ... | ... | @@ -691,20 +690,14 @@ pub fn appendZigProcessFlags( |
| 691 | 690 | }, |
| 692 | 691 | .other_step => |other| { |
| 693 | 692 | if (other.generated_h) |header| { |
| 694 | try zig_args.append("-isystem"); | |
| 695 | try zig_args.append(std.fs.path.dirname(header.path.?).?); | |
| 693 | try zig_args.appendSlice(&.{ "-isystem", std.fs.path.dirname(header.getPath()).? }); | |
| 696 | 694 | } |
| 697 | if (other.installed_headers.items.len > 0) { | |
| 698 | try zig_args.append("-I"); | |
| 699 | try zig_args.append(b.pathJoin(&.{ | |
| 700 | other.step.owner.install_prefix, "include", | |
| 701 | })); | |
| 695 | if (other.installed_headers_include_tree) |include_tree| { | |
| 696 | try zig_args.appendSlice(&.{ "-I", include_tree.generated_directory.getPath() }); | |
| 702 | 697 | } |
| 703 | 698 | }, |
| 704 | 699 | .config_header_step => |config_header| { |
| 705 | const full_file_path = config_header.output_file.path.?; | |
| 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 }); | |
| 700 | try zig_args.appendSlice(&.{ "-I", std.fs.path.dirname(config_header.output_file.getPath()).? }); | |
| 708 | 701 | }, |
| 709 | 702 | } |
| 710 | 703 | } |
| ... | ... | @@ -752,9 +745,7 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void { |
| 752 | 745 | m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM"); |
| 753 | 746 | m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM"); |
| 754 | 747 | |
| 755 | for (other.installed_headers.items) |install_step| { | |
| 756 | addStepDependenciesOnly(m, install_step); | |
| 757 | } | |
| 748 | addLazyPathDependenciesOnly(m, other.getEmittedIncludeTree()); | |
| 758 | 749 | } |
| 759 | 750 | |
| 760 | 751 | fn requireKnownTarget(m: *Module) std.Target { |
lib/std/Build/Step/Compile.zig+163-66| ... | ... | @@ -59,7 +59,13 @@ test_runner: ?[]const u8, |
| 59 | 59 | test_server_mode: bool, |
| 60 | 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 | 70 | // keep in sync with src/Compilation.zig:RcIncludes |
| 65 | 71 | /// Behavior of automatic detection of include directories when compiling .rc files. |
| ... | ... | @@ -249,6 +255,90 @@ pub const Kind = enum { |
| 249 | 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 | 342 | pub fn create(owner: *std.Build, options: Options) *Compile { |
| 253 | 343 | const name = owner.dupe(options.name); |
| 254 | 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 | 398 | .out_lib_filename = undefined, |
| 309 | 399 | .major_only_filename = null, |
| 310 | 400 | .name_only_filename = null, |
| 311 | .installed_headers = ArrayList(*Step).init(owner.allocator), | |
| 401 | .installed_headers = ArrayList(HeaderInstallation).init(owner.allocator), | |
| 312 | 402 | .zig_lib_dir = null, |
| 313 | 403 | .exec_cmd_args = null, |
| 314 | 404 | .filters = options.filters, |
| ... | ... | @@ -380,78 +470,85 @@ pub fn create(owner: *std.Build, options: Options) *Compile { |
| 380 | 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 | 477 | const b = cs.step.owner; |
| 385 | const install_file = b.addInstallHeaderFile(src_path, dest_rel_path); | |
| 386 | b.getInstallStep().dependOn(&install_file.step); | |
| 387 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | |
| 388 | } | |
| 389 | ||
| 390 | pub const InstallConfigHeaderOptions = struct { | |
| 391 | install_dir: InstallDir = .header, | |
| 392 | dest_rel_path: ?[]const u8 = null, | |
| 393 | }; | |
| 394 | ||
| 395 | pub fn installConfigHeader( | |
| 396 | cs: *Compile, | |
| 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 | ||
| 478 | const installation: HeaderInstallation = .{ .file = .{ | |
| 479 | .source = source.dupe(b), | |
| 480 | .dest_rel_path = b.dupePath(dest_rel_path), | |
| 481 | } }; | |
| 482 | cs.installed_headers.append(installation) catch @panic("OOM"); | |
| 483 | cs.addHeaderInstallationToIncludeTree(installation); | |
| 484 | installation.getSource().addStepDependencies(&cs.step); | |
| 485 | } | |
| 486 | ||
| 487 | /// Marks headers from the specified directory for installation alongside this artifact. | |
| 488 | /// When a module links with this artifact, all headers marked for installation are added to that | |
| 489 | /// module's include search path. | |
| 412 | 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 | 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 | 495 | ) void { |
| 428 | 496 | const b = cs.step.owner; |
| 429 | const install_dir = b.addInstallDirectory(options); | |
| 430 | b.getInstallStep().dependOn(&install_dir.step); | |
| 431 | cs.installed_headers.append(&install_dir.step) catch @panic("OOM"); | |
| 497 | const installation: HeaderInstallation = .{ .directory = .{ | |
| 498 | .source = source.dupe(b), | |
| 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 { | |
| 435 | assert(l.kind == .lib); | |
| 541 | pub fn getEmittedIncludeTree(cs: *Compile) LazyPath { | |
| 542 | if (cs.installed_headers_include_tree) |wf| return wf.getDirectory(); | |
| 436 | 543 | const b = cs.step.owner; |
| 437 | const install_step = b.getInstallStep(); | |
| 438 | // Copy each element from installed_headers, modifying the builder | |
| 439 | // to be the new parent's builder. | |
| 440 | for (l.installed_headers.items) |step| { | |
| 441 | const step_copy = switch (step.id) { | |
| 442 | inline .install_file, .install_dir => |id| blk: { | |
| 443 | const T = id.Type(); | |
| 444 | const ptr = b.allocator.create(T) catch @panic("OOM"); | |
| 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"); | |
| 544 | const wf = b.addWriteFiles(); | |
| 545 | cs.installed_headers_include_tree = wf; | |
| 546 | for (cs.installed_headers.items) |installation| { | |
| 547 | cs.addHeaderInstallationToIncludeTree(installation); | |
| 548 | } | |
| 549 | // The compile step itself does not need to depend on the write files step, | |
| 550 | // only dependent modules do. | |
| 551 | return wf.getDirectory(); | |
| 455 | 552 | } |
| 456 | 553 | |
| 457 | 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 | 77 | }, |
| 78 | 78 | .h_dir = switch (options.h_dir) { |
| 79 | 79 | .disabled => null, |
| 80 | // https://github.com/ziglang/zig/issues/9698 | |
| 81 | .default => null, | |
| 82 | //.default => switch (artifact.kind) { | |
| 83 | // .lib => .header, | |
| 84 | // else => null, | |
| 85 | //}, | |
| 80 | .default => switch (artifact.kind) { | |
| 81 | .lib => .header, | |
| 82 | else => null, | |
| 83 | }, | |
| 86 | 84 | .override => |o| o, |
| 87 | 85 | }, |
| 88 | 86 | .implib_dir = switch (options.implib_dir) { |
| ... | ... | @@ -113,7 +111,8 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins |
| 113 | 111 | |
| 114 | 112 | if (self.dest_dir != null) self.emitted_bin = artifact.getEmittedBin(); |
| 115 | 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 | 116 | if (self.implib_dir != null) self.emitted_implib = artifact.getEmittedImplib(); |
| 118 | 117 | |
| 119 | 118 | return self; |
| ... | ... | @@ -122,14 +121,14 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins |
| 122 | 121 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 123 | 122 | _ = prog_node; |
| 124 | 123 | const self: *InstallArtifact = @fieldParentPtr("step", step); |
| 125 | const dest_builder = step.owner; | |
| 124 | const b = step.owner; | |
| 126 | 125 | const cwd = fs.cwd(); |
| 127 | 126 | |
| 128 | 127 | var all_cached = true; |
| 129 | 128 | |
| 130 | 129 | if (self.dest_dir) |dest_dir| { |
| 131 | const full_dest_path = dest_builder.getInstallPath(dest_dir, self.dest_sub_path); | |
| 132 | const full_src_path = self.emitted_bin.?.getPath2(step.owner, step); | |
| 130 | const full_dest_path = b.getInstallPath(dest_dir, self.dest_sub_path); | |
| 131 | const full_src_path = self.emitted_bin.?.getPath2(b, step); | |
| 133 | 132 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { |
| 134 | 133 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ |
| 135 | 134 | full_src_path, full_dest_path, @errorName(err), |
| ... | ... | @@ -145,8 +144,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 145 | 144 | } |
| 146 | 145 | |
| 147 | 146 | if (self.implib_dir) |implib_dir| { |
| 148 | const full_src_path = self.emitted_implib.?.getPath2(step.owner, step); | |
| 149 | const full_implib_path = dest_builder.getInstallPath(implib_dir, fs.path.basename(full_src_path)); | |
| 147 | const full_src_path = self.emitted_implib.?.getPath2(b, step); | |
| 148 | const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(full_src_path)); | |
| 150 | 149 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { |
| 151 | 150 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ |
| 152 | 151 | full_src_path, full_implib_path, @errorName(err), |
| ... | ... | @@ -156,8 +155,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 156 | 155 | } |
| 157 | 156 | |
| 158 | 157 | if (self.pdb_dir) |pdb_dir| { |
| 159 | const full_src_path = self.emitted_pdb.?.getPath2(step.owner, step); | |
| 160 | const full_pdb_path = dest_builder.getInstallPath(pdb_dir, fs.path.basename(full_src_path)); | |
| 158 | const full_src_path = self.emitted_pdb.?.getPath2(b, step); | |
| 159 | const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(full_src_path)); | |
| 161 | 160 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { |
| 162 | 161 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ |
| 163 | 162 | full_src_path, full_pdb_path, @errorName(err), |
| ... | ... | @@ -167,14 +166,68 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 167 | 166 | } |
| 168 | 167 | |
| 169 | 168 | if (self.h_dir) |h_dir| { |
| 170 | const full_src_path = self.emitted_h.?.getPath2(step.owner, step); | |
| 171 | const full_h_path = dest_builder.getInstallPath(h_dir, fs.path.basename(full_src_path)); | |
| 172 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { | |
| 173 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | |
| 174 | full_src_path, full_h_path, @errorName(err), | |
| 175 | }); | |
| 169 | if (self.emitted_h) |emitted_h| { | |
| 170 | const full_src_path = emitted_h.getPath2(b, step); | |
| 171 | const full_h_path = b.getInstallPath(h_dir, fs.path.basename(full_src_path)); | |
| 172 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { | |
| 173 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | |
| 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 | 233 | step.result_cached = all_cached; |
lib/std/Build/Step/InstallDir.zig+9-14| ... | ... | @@ -8,9 +8,6 @@ const InstallDirStep = @This(); |
| 8 | 8 | |
| 9 | 9 | step: Step, |
| 10 | 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 | 12 | pub const base_id = .install_dir; |
| 16 | 13 | |
| ... | ... | @@ -55,7 +52,6 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep { |
| 55 | 52 | .makeFn = make, |
| 56 | 53 | }), |
| 57 | 54 | .options = options.dupe(owner), |
| 58 | .dest_builder = owner, | |
| 59 | 55 | }; |
| 60 | 56 | options.source_dir.addStepDependencies(&self.step); |
| 61 | 57 | return self; |
| ... | ... | @@ -63,15 +59,14 @@ pub fn create(owner: *std.Build, options: Options) *InstallDirStep { |
| 63 | 59 | |
| 64 | 60 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 65 | 61 | _ = prog_node; |
| 62 | const b = step.owner; | |
| 66 | 63 | const self: *InstallDirStep = @fieldParentPtr("step", step); |
| 67 | const dest_builder = self.dest_builder; | |
| 68 | const arena = dest_builder.allocator; | |
| 69 | const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir); | |
| 70 | const src_builder = self.step.owner; | |
| 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| { | |
| 64 | const arena = b.allocator; | |
| 65 | const dest_prefix = b.getInstallPath(self.options.install_dir, self.options.install_subdir); | |
| 66 | const src_dir_path = self.options.source_dir.getPath2(b, step); | |
| 67 | var src_dir = b.build_root.handle.openDir(src_dir_path, .{ .iterate = true }) catch |err| { | |
| 73 | 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 | 72 | defer src_dir.close(); |
| ... | ... | @@ -104,20 +99,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 104 | 99 | .file => { |
| 105 | 100 | for (self.options.blank_extensions) |ext| { |
| 106 | 101 | if (mem.endsWith(u8, entry.path, ext)) { |
| 107 | try dest_builder.truncateFile(dest_path); | |
| 102 | try b.truncateFile(dest_path); | |
| 108 | 103 | continue :next_entry; |
| 109 | 104 | } |
| 110 | 105 | } |
| 111 | 106 | |
| 112 | 107 | const prev_status = fs.Dir.updateFile( |
| 113 | src_builder.build_root.handle, | |
| 108 | b.build_root.handle, | |
| 114 | 109 | src_sub_path, |
| 115 | 110 | cwd, |
| 116 | 111 | dest_path, |
| 117 | 112 | .{}, |
| 118 | 113 | ) catch |err| { |
| 119 | 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 | 118 | all_cached = all_cached and prev_status == .fresh; |
lib/std/Build/Step/InstallFile.zig+3-8| ... | ... | @@ -11,9 +11,6 @@ step: Step, |
| 11 | 11 | source: LazyPath, |
| 12 | 12 | dir: InstallDir, |
| 13 | 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 | 15 | pub fn create( |
| 19 | 16 | owner: *std.Build, |
| ... | ... | @@ -34,7 +31,6 @@ pub fn create( |
| 34 | 31 | .source = source.dupe(owner), |
| 35 | 32 | .dir = dir.dupe(owner), |
| 36 | 33 | .dest_rel_path = owner.dupePath(dest_rel_path), |
| 37 | .dest_builder = owner, | |
| 38 | 34 | }; |
| 39 | 35 | source.addStepDependencies(&self.step); |
| 40 | 36 | return self; |
| ... | ... | @@ -42,11 +38,10 @@ pub fn create( |
| 42 | 38 | |
| 43 | 39 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 44 | 40 | _ = prog_node; |
| 45 | const src_builder = step.owner; | |
| 41 | const b = step.owner; | |
| 46 | 42 | const self: *InstallFile = @fieldParentPtr("step", step); |
| 47 | const dest_builder = self.dest_builder; | |
| 48 | const full_src_path = self.source.getPath2(src_builder, step); | |
| 49 | const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path); | |
| 43 | const full_src_path = self.source.getPath2(b, step); | |
| 44 | const full_dest_path = b.getInstallPath(self.dir, self.dest_rel_path); | |
| 50 | 45 | const cwd = std.fs.cwd(); |
| 51 | 46 | const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { |
| 52 | 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 | 15 | const WriteFile = @This(); |
| 16 | 16 | |
| 17 | 17 | step: Step, |
| 18 | /// The elements here are pointers because we need stable pointers for the | |
| 19 | /// GeneratedFile field. | |
| 18 | ||
| 19 | // The elements here are pointers because we need stable pointers for the GeneratedFile field. | |
| 20 | 20 | files: std.ArrayListUnmanaged(*File), |
| 21 | directories: std.ArrayListUnmanaged(*Directory), | |
| 22 | ||
| 21 | 23 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), |
| 22 | 24 | generated_directory: std.Build.GeneratedFile, |
| 23 | 25 | |
| ... | ... | @@ -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 | 65 | pub const OutputSourceFile = struct { |
| 37 | 66 | contents: Contents, |
| 38 | 67 | sub_path: []const u8, |
| ... | ... | @@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile { |
| 53 | 82 | .makeFn = make, |
| 54 | 83 | }), |
| 55 | 84 | .files = .{}, |
| 85 | .directories = .{}, | |
| 56 | 86 | .output_source_files = .{}, |
| 57 | 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 | 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 | 154 | /// A path relative to the package root. |
| 100 | 155 | /// Be careful with this because it updates source files. This should not be |
| 101 | 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 | 185 | } |
| 131 | 186 | |
| 132 | 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 | 189 | // First time adding a file; update name. |
| 135 | 190 | if (std.mem.eql(u8, wf.step.name, "WriteFile")) { |
| 136 | 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 | 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 | 279 | if (try step.cacheHit(&man)) { |
| 214 | 280 | const digest = man.final(); |
| ... | ... | @@ -233,6 +299,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 233 | 299 | }; |
| 234 | 300 | defer cache_dir.close(); |
| 235 | 301 | |
| 302 | const cwd = fs.cwd(); | |
| 303 | ||
| 236 | 304 | for (wf.files.items) |file| { |
| 237 | 305 | if (fs.path.dirname(file.sub_path)) |dirname| { |
| 238 | 306 | cache_dir.makePath(dirname) catch |err| { |
| ... | ... | @@ -252,7 +320,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 252 | 320 | .copy => |file_source| { |
| 253 | 321 | const source_path = file_source.getPath(b); |
| 254 | 322 | const prev_status = fs.Dir.updateFile( |
| 255 | fs.cwd(), | |
| 323 | cwd, | |
| 256 | 324 | source_path, |
| 257 | 325 | cache_dir, |
| 258 | 326 | file.sub_path, |
| ... | ... | @@ -279,6 +347,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 279 | 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 | 409 | try step.writeManifest(&man); |
| 284 | 410 | } |
test/standalone.zig+4| ... | ... | @@ -266,6 +266,10 @@ pub const build_cases = [_]BuildCase{ |
| 266 | 266 | .build_root = "test/standalone/depend_on_main_mod", |
| 267 | 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 | 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" |