// demo2.2
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="downloader",.target=target,.optimize=optimize,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("download.c"),.flags=&.{}});exe.linkSystemLibrary("curl");b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
//demo 2.3
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="example",.target=target,.optimize=optimize,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("bass-player.c"),.flags=&.{}});exe.linkLibC();// 还是一步步看源代码,找新的函数,addIncludeDir,addLibDir ->new function
exe.addIncludePath(std.build.LazyPath.relative("bass/linux"));exe.addLibraryPath(std.build.LazyPath.relative("bass/linux/x64"));exe.linkSystemLibrary("bass");b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
addIncludePath 和 addLibraryPath 都可以被多次调用,以向编译器添加多个路径。这些函数不仅会影响 C 代码,还会影响 Zig 代码,因此 @cImport 可以访问包含路径中的所有头文件。
每个文件的包含路径
因此,如果我们需要为每个 C 文件设置不同的包含路径,我们就需要用不同的方法来解决这个问题:
由于我们仍然可以通过 addCSourceFile 传递任何 C 编译器标志,因此我们也可以在这里手动设置包含目录。
//demo2.4
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="example",.target=target,.optimize=optimize,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("multi-main.c"),.flags=&.{}});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("multi.c"),.flags=&.{"-I","inc1"}});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("multi.c"),.flags=&.{"-I","inc2"}});b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
//demo2.5
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="example",.target=target,.optimize=optimize,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("main.c"),.flags=&.{}});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("buffer.cc"),.flags=&.{}});exe.linkLibCpp();b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
如你所见,我们还需要调用 linkLibCpp,它将链接 Zig 附带的 c++ 标准库。
这就是构建 C++ 文件所需的全部知识,没有什么更神奇的了。
指定语言版本
试想一下,如果你创建了一个庞大的项目,其中的 C 或 C++ 文件有新有旧,而且可能是用不同的语言标准编写的。为此,我们可以使用编译器标志来传递 -std=c90 或 -std=c++98:
//demo2.6
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="example",.target=target,.optimize=optimize,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("main.c"),.flags=&.{"-std=c90"}});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("buffer.cc"),.flags=&.{"-std=c++17"}});exe.linkLibCpp();b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
条件编译
与 Zig 相比,C 和 C++ 的条件编译方式非常繁琐。由于缺乏惰性求值的功能,有时必须根据目标环境来包含/排除文件。你还必须提供宏定义来启用/禁用某些项目功能。
//demo2.7
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constuse_platform_io=b.option(bool,"platform-io","Uses the native api instead of the C wrapper")orelsetrue;constexe=b.addExecutable(.{.name="example",.target=target,.optimize=optimize,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("print-main.c"),.flags=&.{}});if(use_platform_io){exe.defineCMacro("USE_PLATFORM_IO",null);if(exe.target.isWindows()){exe.addCSourceFile(.{.file=std.build.LazyPath.relative("print-windows.c"),.flags=&.{}});}else{exe.addCSourceFile(.{.file=std.build.LazyPath.relative("print-unix.c"),.flags=&.{}});}}exe.linkLibC();b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
//demo2.8
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="example",.target=target,.optimize=optimize,});constflags=.{"-Wall","-Wextra","-Werror=return-type",};constcflags=flags++.{"-std=c99"};constcppflags=cflags++.{"-std=c++17","-stdlib=libc++","-fno-exceptions",};exe.addCSourceFile(.{.file=std.build.LazyPath.relative("main.c"),.flags=&cflags,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("buffer.cc"),.flags=&cppflags,});exe.linkLibC();exe.linkLibCpp();b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
//demo2.9
conststd=@import("std");pubfnbuild(b:*std.build.Builder)!void{varsources=std.ArrayList([]constu8).init(b.allocator);// Search for all C/C++ files in `src` and add them
{vardir=trystd.fs.cwd().openIterableDir(".",.{.access_sub_paths=true});varwalker=trydir.walk(b.allocator);deferwalker.deinit();constallowed_exts=[_][]constu8{".c",".cpp",".cxx",".c++",".cc"};while(trywalker.next())|entry|{constext=std.fs.path.extension(entry.basename);constinclude_file=for(allowed_exts)|e|{if(std.mem.eql(u8,ext,e))breaktrue;}elsefalse;if(include_file){// we have to clone the path as walker.next() or walker.deinit() will override/kill it
trysources.append(b.dupe(entry.path));}}}consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="example",.target=target,.optimize=optimize,});exe.addCSourceFiles(sources.items,&.{});exe.linkLibC();exe.linkLibCpp();b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
//demo2.10
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="example",.target=target,.optimize=optimize,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("main.m"),.flags=&.{},});exe.linkFramework("Foundation");b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
//demo2.11
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constexe=b.addExecutable(.{.name="example",.root_source_file=.{.path="main.zig"},.target=target,.optimize=optimize,});exe.addCSourceFile(.{.file=std.build.LazyPath.relative("buffer.c"),.flags=&.{},});exe.linkLibC();b.installArtifact(exe);construn_cmd=b.addRunArtifact(exe);run_cmd.step.dependOn(b.getInstallStep());if(b.args)|args|{run_cmd.addArgs(args);}construn_step=b.step("run","Run the app");run_step.dependOn(&run_cmd.step);}
这就是需要做的一切!是这样吗?
实际上,有一种情况现在还没有得到很好的支持:
您应用程序的入口点现在必须在 Zig 代码中,因为根文件必须导出一个 pub fn main(…) ……。
因此,如果你想将 C 项目中的代码移植到 Zig 中,你必须将 argc 和 argv 转发到你的 C 代码中,并将 C 代码中的 main 重命名为其他函数(例如 oldMain),然后在 Zig 中调用它。如果需要 argc 和 argv,可以通过 std.process.argsAlloc 获取。或者更好: 在 Zig 中重写你的入口点,然后从你的项目中移除一些 C 语言!