authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-16 14:14:11-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-17 06:47:20-04:00
logd16ce67106796011706e9f3653bb843c21c9d708
treef5c767fa0ebd503f78ba47b638366c04d380b9a2
parent3a8490f7e95faeff6be9b65c93cd55c8dd11ed1e

zig build system: ability to link against dynamic library step


4 files changed, 256 insertions(+), 20 deletions(-)

std/build.zig+234-20
...@@ -10,6 +10,7 @@ const StdIo = os.ChildProcess.StdIo;...@@ -10,6 +10,7 @@ const StdIo = os.ChildProcess.StdIo;
10const Term = os.ChildProcess.Term;10const Term = os.ChildProcess.Term;
11const BufSet = @import("buf_set.zig").BufSet;11const BufSet = @import("buf_set.zig").BufSet;
12const BufMap = @import("buf_map.zig").BufMap;12const BufMap = @import("buf_map.zig").BufMap;
13const fmt = @import("fmt.zig");
1314
14error ExtraArg;15error ExtraArg;
15error UncleanExit;16error UncleanExit;
...@@ -32,7 +33,7 @@ pub const Builder = struct {...@@ -32,7 +33,7 @@ pub const Builder = struct {
32 env_map: BufMap,33 env_map: BufMap,
33 top_level_steps: List(&TopLevelStep),34 top_level_steps: List(&TopLevelStep),
34 prefix: []const u8,35 prefix: []const u8,
35 out_dir: []const u8,36 out_dir: []u8,
3637
37 const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);38 const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);
38 const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);39 const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);
...@@ -84,7 +85,7 @@ pub const Builder = struct {...@@ -84,7 +85,7 @@ pub const Builder = struct {
84 .default_step = undefined,85 .default_step = undefined,
85 .env_map = %%os.getEnvMap(allocator),86 .env_map = %%os.getEnvMap(allocator),
86 .prefix = undefined,87 .prefix = undefined,
87 .out_dir = ".", // TODO organize88 .out_dir = %%os.getCwd(allocator),
88 };89 };
89 self.processNixOSEnvVars();90 self.processNixOSEnvVars();
90 self.default_step = self.step("default", "Build the project");91 self.default_step = self.step("default", "Build the project");
...@@ -92,6 +93,7 @@ pub const Builder = struct {...@@ -92,6 +93,7 @@ pub const Builder = struct {
92 }93 }
9394
94 pub fn deinit(self: &Builder) {95 pub fn deinit(self: &Builder) {
96 self.allocator.free(self.out_dir);
95 self.lib_paths.deinit();97 self.lib_paths.deinit();
96 self.include_paths.deinit();98 self.include_paths.deinit();
97 self.rpaths.deinit();99 self.rpaths.deinit();
...@@ -410,6 +412,17 @@ const CrossTarget = struct {...@@ -410,6 +412,17 @@ const CrossTarget = struct {
410const Target = enum {412const Target = enum {
411 Native,413 Native,
412 Cross: CrossTarget,414 Cross: CrossTarget,
415
416 pub fn oFileExt(self: &const Target) -> []const u8 {
417 const environ = switch (*self) {
418 Target.Native => @compileVar("environ"),
419 Target.Cross => |t| t.environ,
420 };
421 return switch (environ) {
422 Environ.msvc => ".obj",
423 else => ".o",
424 };
425 }
413};426};
414427
415const LinkerScript = enum {428const LinkerScript = enum {
...@@ -562,28 +575,23 @@ const Exe = struct {...@@ -562,28 +575,23 @@ const Exe = struct {
562 var child = os.ChildProcess.spawn(builder.zig_exe, zig_args.toSliceConst(), &builder.env_map,575 var child = os.ChildProcess.spawn(builder.zig_exe, zig_args.toSliceConst(), &builder.env_map,
563 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, builder.allocator)576 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, builder.allocator)
564 %% |err| debug.panic("Unable to spawn zig compiler: {}\n", @errorName(err));577 %% |err| debug.panic("Unable to spawn zig compiler: {}\n", @errorName(err));
565 const term = %%child.wait();578 %return waitForCleanExit(&child);
566 switch (term) {
567 Term.Clean => |code| {
568 if (code != 0) {
569 return error.UncleanExit;
570 }
571 },
572 else => {
573 return error.UncleanExit;
574 },
575 };
576 }579 }
577};580};
578581
579const CLibrary = struct {582const CLibrary = struct {
580 step: Step,583 step: Step,
581 name: []const u8,584 name: []const u8,
585 out_filename: []const u8,
582 static: bool,586 static: bool,
583 version: Version,587 version: Version,
584 cflags: List([]const u8),588 cflags: List([]const u8),
585 source_files: List([]const u8),589 source_files: List([]const u8),
590 object_files: List([]const u8),
586 link_libs: BufSet,591 link_libs: BufSet,
592 target: Target,
593 builder: &Builder,
594 include_dirs: List([]const u8),
587595
588 pub fn initShared(builder: &Builder, name: []const u8, version: &const Version) -> CLibrary {596 pub fn initShared(builder: &Builder, name: []const u8, version: &const Version) -> CLibrary {
589 return init(builder, name, version, false);597 return init(builder, name, version, false);
...@@ -594,14 +602,30 @@ const CLibrary = struct {...@@ -594,14 +602,30 @@ const CLibrary = struct {
594 }602 }
595603
596 fn init(builder: &Builder, name: []const u8, version: &const Version, static: bool) -> CLibrary {604 fn init(builder: &Builder, name: []const u8, version: &const Version, static: bool) -> CLibrary {
597 CLibrary {605 var clib = CLibrary {
606 .builder = builder,
598 .name = name,607 .name = name,
599 .version = *version,608 .version = *version,
600 .static = static,609 .static = static,
610 .target = Target.Native,
601 .cflags = List([]const u8).init(builder.allocator),611 .cflags = List([]const u8).init(builder.allocator),
602 .source_files = List([]const u8).init(builder.allocator),612 .source_files = List([]const u8).init(builder.allocator),
613 .object_files = List([]const u8).init(builder.allocator),
603 .step = Step.init(name, builder.allocator, make),614 .step = Step.init(name, builder.allocator, make),
604 .link_libs = BufSet.init(builder.allocator),615 .link_libs = BufSet.init(builder.allocator),
616 .include_dirs = List([]const u8).init(builder.allocator),
617 .out_filename = undefined,
618 };
619 clib.computeOutFileName();
620 return clib;
621 }
622
623 fn computeOutFileName(self: &CLibrary) {
624 if (self.static) {
625 self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.a", self.name);
626 } else {
627 self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.so.{d}.{d}.{d}",
628 self.name, self.version.major, self.version.minor, self.version.patch);
605 }629 }
606 }630 }
607631
...@@ -618,6 +642,14 @@ const CLibrary = struct {...@@ -618,6 +642,14 @@ const CLibrary = struct {
618 %%self.source_files.append(file);642 %%self.source_files.append(file);
619 }643 }
620644
645 pub fn addObjectFile(self: &CLibrary, file: []const u8) {
646 %%self.object_files.append(file);
647 }
648
649 pub fn addIncludeDir(self: &CLibrary, path: []const u8) {
650 %%self.include_dirs.append(path);
651 }
652
621 pub fn addCompileFlagsForRelease(self: &CLibrary, release: bool) {653 pub fn addCompileFlagsForRelease(self: &CLibrary, release: bool) {
622 if (release) {654 if (release) {
623 %%self.cflags.append("-g");655 %%self.cflags.append("-g");
...@@ -637,29 +669,115 @@ const CLibrary = struct {...@@ -637,29 +669,115 @@ const CLibrary = struct {
637 // TODO issue #320669 // TODO issue #320
638 //const self = @fieldParentPtr(CLibrary, "step", step);670 //const self = @fieldParentPtr(CLibrary, "step", step);
639 const self = @ptrcast(&CLibrary, step);671 const self = @ptrcast(&CLibrary, step);
672 const cc = os.getEnv("CC") ?? "cc";
673 const builder = self.builder;
674
675 var cc_args = List([]const u8).init(builder.allocator);
676 defer cc_args.deinit();
677
678 for (self.source_files.toSliceConst()) |source_file| {
679 %%cc_args.resize(0);
680
681 if (!self.static) {
682 %%cc_args.append("-fPIC");
683 }
684
685 %%cc_args.append("-c");
686 %%cc_args.append(source_file);
640687
641 const cc = os.getEnv("CC") ?? {688 // TODO don't dump the .o file in the same place as the source file
642 %%io.stderr.printf("Unable to find C compiler\n");689 const o_file = %%fmt.allocPrint(builder.allocator, "{}{}", source_file, self.target.oFileExt());
643 return error.NoCompilerFound;690 defer builder.allocator.free(o_file);
691 %%cc_args.append("-o");
692 %%cc_args.append(o_file);
693
694 for (self.cflags.toSliceConst()) |cflag| {
695 %%cc_args.append(cflag);
696 }
697
698 for (self.include_dirs.toSliceConst()) |dir| {
699 %%cc_args.append("-I");
700 %%cc_args.append(dir);
701 }
702
703 if (builder.verbose) {
704 printInvocation(cc, cc_args);
705 }
706
707 var child = os.ChildProcess.spawn(cc, cc_args.toSliceConst(), &builder.env_map,
708 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, builder.allocator)
709 %% |err| debug.panic("Unable to spawn compiler: {}\n", @errorName(err));
710 %return waitForCleanExit(&child);
711
712 %%self.object_files.append(o_file);
713 }
714
715 if (self.static) {
716 debug.panic("TODO static library");
717 } else {
718 %%cc_args.resize(0);
719
720 %%cc_args.append("-fPIC");
721 %%cc_args.append("-shared");
722
723 const soname_arg = %%fmt.allocPrint(builder.allocator, "-Wl,-soname,lib{}.so.{d}",
724 self.name, self.version.major);
725 defer builder.allocator.free(soname_arg);
726 %%cc_args.append(soname_arg);
727
728 %%cc_args.append("-o");
729 %%cc_args.append(self.out_filename);
730
731 for (self.object_files.toSliceConst()) |object_file| {
732 %%cc_args.append(object_file);
733 }
734
735 if (builder.verbose) {
736 printInvocation(cc, cc_args);
737 }
738
739 var child = os.ChildProcess.spawn(cc, cc_args.toSliceConst(), &builder.env_map,
740 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, builder.allocator)
741 %% |err| debug.panic("Unable to spawn compiler: {}\n", @errorName(err));
742 %return waitForCleanExit(&child);
743 }
744 }
745
746 pub fn setTarget(self: &CLibrary, target_arch: Arch, target_os: Os, target_environ: Environ) {
747 self.target = Target.Cross {
748 CrossTarget {
749 .arch = target_arch,
750 .os = target_os,
751 .environ = target_environ,
752 }
644 };753 };
645 %%io.stderr.printf("TODO: build c library\n");
646 }754 }
647};755};
648756
649const CExecutable = struct {757const CExecutable = struct {
650 step: Step,758 step: Step,
759 builder: &Builder,
651 name: []const u8,760 name: []const u8,
652 cflags: List([]const u8),761 cflags: List([]const u8),
653 source_files: List([]const u8),762 source_files: List([]const u8),
763 object_files: List([]const u8),
764 full_path_libs: List([]const u8),
654 link_libs: BufSet,765 link_libs: BufSet,
766 target: Target,
767 include_dirs: List([]const u8),
655768
656 pub fn init(builder: &Builder, name: []const u8) -> CExecutable {769 pub fn init(builder: &Builder, name: []const u8) -> CExecutable {
657 CExecutable {770 CExecutable {
771 .builder = builder,
658 .name = name,772 .name = name,
773 .target = Target.Native,
659 .cflags = List([]const u8).init(builder.allocator),774 .cflags = List([]const u8).init(builder.allocator),
660 .source_files = List([]const u8).init(builder.allocator),775 .source_files = List([]const u8).init(builder.allocator),
776 .object_files = List([]const u8).init(builder.allocator),
777 .full_path_libs = List([]const u8).init(builder.allocator),
661 .step = Step.init(name, builder.allocator, make),778 .step = Step.init(name, builder.allocator, make),
662 .link_libs = BufSet.init(builder.allocator),779 .link_libs = BufSet.init(builder.allocator),
780 .include_dirs = List([]const u8).init(builder.allocator),
663 }781 }
664 }782 }
665783
...@@ -669,13 +787,21 @@ const CExecutable = struct {...@@ -669,13 +787,21 @@ const CExecutable = struct {
669787
670 pub fn linkCLibrary(self: &CExecutable, clib: &CLibrary) {788 pub fn linkCLibrary(self: &CExecutable, clib: &CLibrary) {
671 self.step.dependOn(&clib.step);789 self.step.dependOn(&clib.step);
672 %%self.link_libs.put(clib.name);790 %%self.full_path_libs.append(clib.out_filename);
673 }791 }
674792
675 pub fn addSourceFile(self: &CExecutable, file: []const u8) {793 pub fn addSourceFile(self: &CExecutable, file: []const u8) {
676 %%self.source_files.append(file);794 %%self.source_files.append(file);
677 }795 }
678796
797 pub fn addObjectFile(self: &CExecutable, file: []const u8) {
798 %%self.object_files.append(file);
799 }
800
801 pub fn addIncludeDir(self: &CExecutable, path: []const u8) {
802 %%self.include_dirs.append(path);
803 }
804
679 pub fn addCompileFlagsForRelease(self: &CExecutable, release: bool) {805 pub fn addCompileFlagsForRelease(self: &CExecutable, release: bool) {
680 if (release) {806 if (release) {
681 %%self.cflags.append("-g");807 %%self.cflags.append("-g");
...@@ -695,8 +821,82 @@ const CExecutable = struct {...@@ -695,8 +821,82 @@ const CExecutable = struct {
695 // TODO issue #320821 // TODO issue #320
696 //const self = @fieldParentPtr(CExecutable, "step", step);822 //const self = @fieldParentPtr(CExecutable, "step", step);
697 const self = @ptrcast(&CExecutable, step);823 const self = @ptrcast(&CExecutable, step);
824 const cc = os.getEnv("CC") ?? "cc";
825 const builder = self.builder;
826
827 var cc_args = List([]const u8).init(builder.allocator);
828 defer cc_args.deinit();
829
830 for (self.source_files.toSliceConst()) |source_file| {
831 %%cc_args.resize(0);
832
833 %%cc_args.append("-c");
834 %%cc_args.append(source_file);
835
836 // TODO don't dump the .o file in the same place as the source file
837 const o_file = %%fmt.allocPrint(builder.allocator, "{}{}", source_file, self.target.oFileExt());
838 defer builder.allocator.free(o_file);
839 %%cc_args.append("-o");
840 %%cc_args.append(o_file);
841
842 for (self.cflags.toSliceConst()) |cflag| {
843 %%cc_args.append(cflag);
844 }
845
846 for (self.include_dirs.toSliceConst()) |dir| {
847 %%cc_args.append("-I");
848 %%cc_args.append(dir);
849 }
850
851 if (builder.verbose) {
852 printInvocation(cc, cc_args);
853 }
854
855 var child = os.ChildProcess.spawn(cc, cc_args.toSliceConst(), &builder.env_map,
856 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, builder.allocator)
857 %% |err| debug.panic("Unable to spawn compiler: {}\n", @errorName(err));
858 %return waitForCleanExit(&child);
859
860 %%self.object_files.append(o_file);
861 }
862
863 %%cc_args.resize(0);
698864
699 %%io.stderr.printf("TODO: build c exe\n");865 for (self.object_files.toSliceConst()) |object_file| {
866 %%cc_args.append(object_file);
867 }
868
869 %%cc_args.append("-o");
870 %%cc_args.append(self.name);
871
872 const rpath_arg = %%fmt.allocPrint(builder.allocator, "-Wl,-rpath,{}", builder.out_dir);
873 defer builder.allocator.free(rpath_arg);
874 %%cc_args.append(rpath_arg);
875
876 %%cc_args.append("-rdynamic");
877
878 for (self.full_path_libs.toSliceConst()) |full_path_lib| {
879 %%cc_args.append(full_path_lib);
880 }
881
882 if (builder.verbose) {
883 printInvocation(cc, cc_args);
884 }
885
886 var child = os.ChildProcess.spawn(cc, cc_args.toSliceConst(), &builder.env_map,
887 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, builder.allocator)
888 %% |err| debug.panic("Unable to spawn compiler: {}\n", @errorName(err));
889 %return waitForCleanExit(&child);
890 }
891
892 pub fn setTarget(self: &CExecutable, target_arch: Arch, target_os: Os, target_environ: Environ) {
893 self.target = Target.Cross {
894 CrossTarget {
895 .arch = target_arch,
896 .os = target_os,
897 .environ = target_environ,
898 }
899 };
700 }900 }
701};901};
702902
...@@ -770,3 +970,17 @@ fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) {...@@ -770,3 +970,17 @@ fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) {
770 }970 }
771 %%io.stderr.printf("\n");971 %%io.stderr.printf("\n");
772}972}
973
974fn waitForCleanExit(child: &os.ChildProcess) -> %void {
975 const term = %%child.wait();
976 switch (term) {
977 Term.Clean => |code| {
978 if (code != 0) {
979 return error.UncleanExit;
980 }
981 },
982 else => {
983 return error.UncleanExit;
984 },
985 };
986}
std/os/child_process.zig+1
...@@ -41,6 +41,7 @@ pub const ChildProcess = struct {...@@ -41,6 +41,7 @@ pub const ChildProcess = struct {
41 }41 }
42 }42 }
4343
44 /// Blocks until child process terminates and then cleans up all resources.
44 pub fn wait(self: &ChildProcess) -> %Term {45 pub fn wait(self: &ChildProcess) -> %Term {
45 defer {46 defer {
46 os.posixClose(self.err_pipe[0]);47 os.posixClose(self.err_pipe[0]);
std/os/index.zig+17
...@@ -364,3 +364,20 @@ pub const args = struct {...@@ -364,3 +364,20 @@ pub const args = struct {
364 return s[0...cstr.len(s)];364 return s[0...cstr.len(s)];
365 }365 }
366};366};
367
368/// Caller must free the returned memory.
369pub fn getCwd(allocator: &Allocator) -> %[]u8 {
370 var buf = %return allocator.alloc(u8, 1024);
371 %defer allocator.free(buf);
372 while (true) {
373 const err = posix.getErrno(posix.getcwd(buf.ptr, buf.len));
374 if (err == errno.ERANGE) {
375 buf = %return allocator.realloc(u8, buf, buf.len * 2);
376 continue;
377 } else if (err > 0) {
378 return error.Unexpected;
379 }
380
381 return buf;
382 }
383}
std/os/linux.zig+4
...@@ -269,6 +269,10 @@ pub fn fork() -> usize {...@@ -269,6 +269,10 @@ pub fn fork() -> usize {
269 arch.syscall0(arch.SYS_fork)269 arch.syscall0(arch.SYS_fork)
270}270}
271271
272pub fn getcwd(buf: &u8, size: usize) -> usize {
273 arch.syscall2(arch.SYS_getcwd, usize(buf), size)
274}
275
272pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: usize)276pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: usize)
273 -> usize277 -> usize
274{278{