Skip to content

Commit d52cdac

Browse files
committed
std.Build: add an option to CSourceFile to override the language detection.
and change `addAssemblyFile()` to use a `AsmSourceFile` option struct as well. (breaking change) Language is inferred from the file extension, however: - it can be ambiguous. for instance, ".h" is often used for c headers or c++ headers. ".s" (instead of ".S") assembly files may still need the c preprocessor. (ziglang#20655) - a singular file may be interpreted with different languages depending on the context. in "single-file libraries", the source.h file can be both a c-header to include, or compiled as a C file (with a #define as toggle) (ziglang#19423)
1 parent 414fc3b commit d52cdac

File tree

8 files changed

+197
-14
lines changed

8 files changed

+197
-14
lines changed

lib/std/Build/Module.zig

+85-7
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,39 @@ pub const RPath = union(enum) {
4646
special: []const u8,
4747
};
4848

49+
// subset of Compilation.FileExt
50+
pub const AsmSourceLang = enum {
51+
assembly,
52+
assembly_with_cpp,
53+
54+
pub fn getName(lang: @This()) []const u8 {
55+
return switch (lang) {
56+
.assembly => "assembler",
57+
.assembly_with_cpp => "assembler-with-cpp",
58+
};
59+
}
60+
};
61+
62+
pub const AsmSourceFile = struct {
63+
file: LazyPath,
64+
/// if null, deduce the language from the file extension
65+
lang: ?AsmSourceLang = null,
66+
flags: []const []const u8 = &.{},
67+
68+
pub fn dupe(file: AsmSourceFile, b: *std.Build) AsmSourceFile {
69+
return .{
70+
.file = file.file.dupe(b),
71+
.flags = b.dupeStrings(file.flags),
72+
.lang = file.lang,
73+
};
74+
}
75+
};
76+
4977
pub const LinkObject = union(enum) {
5078
static_path: LazyPath,
5179
other_step: *Step.Compile,
5280
system_lib: SystemLib,
53-
assembly_file: LazyPath,
81+
assembly_file: *AsmSourceFile,
5482
c_source_file: *CSourceFile,
5583
c_source_files: *CSourceFiles,
5684
win32_resource_file: *RcSourceFile,
@@ -78,21 +106,68 @@ pub const SystemLib = struct {
78106
pub const SearchStrategy = enum { paths_first, mode_first, no_fallback };
79107
};
80108

109+
/// Supported languages for "zig clang -x <lang>".
110+
// subset of Compilation.FileExt
111+
pub const CSourceLang = enum {
112+
/// "c"
113+
c,
114+
/// "c-header"
115+
h,
116+
/// "c++"
117+
cpp,
118+
/// "c++-header"
119+
hpp,
120+
/// "objective-c"
121+
m,
122+
/// "objective-c-header"
123+
hm,
124+
/// "objective-c++"
125+
mm,
126+
/// "objective-c++-header"
127+
hmm,
128+
/// "assembler"
129+
assembly,
130+
/// "assembler-with-cpp"
131+
assembly_with_cpp,
132+
/// "cuda"
133+
cu,
134+
135+
pub fn getName(lang: @This()) []const u8 {
136+
return switch (lang) {
137+
.assembly => "assembler",
138+
.assembly_with_cpp => "assembler-with-cpp",
139+
.c => "c",
140+
.cpp => "c++",
141+
.h => "c-header",
142+
.hpp => "c++-header",
143+
.hm => "objective-c-header",
144+
.hmm => "objective-c++-header",
145+
.cu => "cuda",
146+
.m => "objective-c",
147+
.mm => "objective-c++",
148+
};
149+
}
150+
};
151+
81152
pub const CSourceFiles = struct {
82153
root: LazyPath,
83154
/// `files` is relative to `root`, which is
84155
/// the build root by default
85156
files: []const []const u8,
157+
/// if null, deduce the language from the file extension
158+
lang: ?CSourceLang = null,
86159
flags: []const []const u8,
87160
};
88161

89162
pub const CSourceFile = struct {
90163
file: LazyPath,
164+
lang: ?CSourceLang = null,
91165
flags: []const []const u8 = &.{},
92166

93167
pub fn dupe(file: CSourceFile, b: *std.Build) CSourceFile {
94168
return .{
95169
.file = file.file.dupe(b),
170+
.lang = file.lang,
96171
.flags = b.dupeStrings(file.flags),
97172
};
98173
}
@@ -324,10 +399,9 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void {
324399
addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree());
325400
},
326401

327-
.static_path,
328-
.assembly_file,
329-
=> |lp| addLazyPathDependencies(m, dependee, lp),
402+
.static_path => |lp| addLazyPathDependencies(m, dependee, lp),
330403

404+
.assembly_file => |x| addLazyPathDependencies(m, dependee, x.file),
331405
.c_source_file => |x| addLazyPathDependencies(m, dependee, x.file),
332406
.win32_resource_file => |x| addLazyPathDependencies(m, dependee, x.file),
333407

@@ -523,6 +597,7 @@ pub const AddCSourceFilesOptions = struct {
523597
/// package that owns the `Compile` step.
524598
root: ?LazyPath = null,
525599
files: []const []const u8,
600+
lang: ?CSourceLang = null,
526601
flags: []const []const u8 = &.{},
527602
};
528603

@@ -544,6 +619,7 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
544619
c_source_files.* = .{
545620
.root = options.root orelse b.path(""),
546621
.files = b.dupeStrings(options.files),
622+
.lang = options.lang,
547623
.flags = b.dupeStrings(options.flags),
548624
};
549625
m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
@@ -579,10 +655,12 @@ pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
579655
}
580656
}
581657

582-
pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
658+
pub fn addAssemblyFile(m: *Module, source: AsmSourceFile) void {
583659
const b = m.owner;
584-
m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
585-
addLazyPathDependenciesOnly(m, source);
660+
const source_file = b.allocator.create(AsmSourceFile) catch @panic("OOM");
661+
source_file.* = source.dupe(b);
662+
m.link_objects.append(b.allocator, .{ .assembly_file = source_file }) catch @panic("OOM");
663+
addLazyPathDependenciesOnly(m, source.file);
586664
}
587665

588666
pub fn addObjectFile(m: *Module, object: LazyPath) void {

lib/std/Build/Step/Compile.zig

+52-4
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,7 @@ pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
907907
return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
908908
}
909909

910-
pub fn addAssemblyFile(compile: *Compile, source: LazyPath) void {
910+
pub fn addAssemblyFile(compile: *Compile, source: Module.AsmSourceFile) void {
911911
compile.root_module.addAssemblyFile(source);
912912
}
913913

@@ -1091,6 +1091,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
10911091

10921092
var prev_has_cflags = false;
10931093
var prev_has_rcflags = false;
1094+
var prev_has_xflag = false;
10941095
var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first;
10951096
var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic;
10961097
// Track the number of positional arguments so that a nice error can be
@@ -1129,6 +1130,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
11291130

11301131
// Inherit dependencies on system libraries and static libraries.
11311132
for (dep.module.link_objects.items) |link_object| {
1133+
if (prev_has_xflag and link_object != .c_source_file and link_object != .c_source_files and link_object != .assembly_file) {
1134+
try zig_args.append("-x");
1135+
try zig_args.append("none");
1136+
prev_has_xflag = false;
1137+
}
1138+
11321139
switch (link_object) {
11331140
.static_path => |static_path| {
11341141
if (my_responsibility) {
@@ -1254,12 +1261,32 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
12541261
.assembly_file => |asm_file| l: {
12551262
if (!my_responsibility) break :l;
12561263

1257-
if (prev_has_cflags) {
1264+
if (asm_file.flags.len == 0) {
1265+
if (prev_has_cflags) {
1266+
try zig_args.append("-cflags");
1267+
try zig_args.append("--");
1268+
prev_has_cflags = false;
1269+
}
1270+
} else {
12581271
try zig_args.append("-cflags");
1272+
for (asm_file.flags) |arg| {
1273+
try zig_args.append(arg);
1274+
}
12591275
try zig_args.append("--");
1260-
prev_has_cflags = false;
1276+
prev_has_cflags = true;
1277+
}
1278+
1279+
if (asm_file.lang) |lang| {
1280+
try zig_args.append("-x");
1281+
try zig_args.append(lang.getName());
1282+
prev_has_xflag = true;
1283+
} else if (prev_has_xflag) {
1284+
try zig_args.append("-x");
1285+
try zig_args.append("none");
1286+
prev_has_xflag = false;
12611287
}
1262-
try zig_args.append(asm_file.getPath2(dep.module.owner, step));
1288+
1289+
try zig_args.append(asm_file.file.getPath2(dep.module.owner, step));
12631290
total_linker_objects += 1;
12641291
},
12651292

@@ -1280,6 +1307,17 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
12801307
try zig_args.append("--");
12811308
prev_has_cflags = true;
12821309
}
1310+
1311+
if (c_source_file.lang) |lang| {
1312+
try zig_args.append("-x");
1313+
try zig_args.append(lang.getName());
1314+
prev_has_xflag = true;
1315+
} else if (prev_has_xflag) {
1316+
try zig_args.append("-x");
1317+
try zig_args.append("none");
1318+
prev_has_xflag = false;
1319+
}
1320+
12831321
try zig_args.append(c_source_file.file.getPath2(dep.module.owner, step));
12841322
total_linker_objects += 1;
12851323
},
@@ -1302,6 +1340,16 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
13021340
prev_has_cflags = true;
13031341
}
13041342

1343+
if (c_source_files.lang) |lang| {
1344+
try zig_args.append("-x");
1345+
try zig_args.append(lang.getName());
1346+
prev_has_xflag = true;
1347+
} else if (prev_has_xflag) {
1348+
try zig_args.append("-x");
1349+
try zig_args.append("none");
1350+
prev_has_xflag = false;
1351+
}
1352+
13051353
const root_path = c_source_files.root.getPath2(dep.module.owner, step);
13061354
for (c_source_files.files) |file| {
13071355
try zig_args.append(b.pathJoin(&.{ root_path, file }));

test/link/link.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ fn addCompileStep(
119119
});
120120
}
121121
if (overlay.asm_source_bytes) |bytes| {
122-
compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes));
122+
compile_step.addAssemblyFile(.{ .file = b.addWriteFiles().add("a.s", bytes) });
123123
}
124124
return compile_step;
125125
}
@@ -147,7 +147,7 @@ pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
147147
const b = comp.step.owner;
148148
const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
149149
const file = WriteFile.create(b).add("a.s", actual_bytes);
150-
comp.addAssemblyFile(file);
150+
comp.addAssemblyFile(.{ .file = file });
151151
}
152152

153153
pub fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {

test/src/CompareOutput.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void {
101101
.target = b.graph.host,
102102
.optimize = .Debug,
103103
});
104-
exe.addAssemblyFile(first_file);
104+
exe.addAssemblyFile(.{ .file = first_file });
105105

106106
const run = b.addRunArtifact(exe);
107107
run.setName(annotated_case_name);

test/standalone/build.zig.zon

+3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@
126126
.c_compiler = .{
127127
.path = "c_compiler",
128128
},
129+
.c_header = .{
130+
.path = "c_header",
131+
},
129132
.pie = .{
130133
.path = "pie",
131134
},

test/standalone/c_header/build.zig

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const std = @import("std");
2+
3+
pub fn build(b: *std.Build) void {
4+
const test_step = b.step("test", "Test it");
5+
b.default_step = test_step;
6+
7+
const target = b.standardTargetOptions(.{});
8+
const optimize = b.standardOptimizeOption(.{});
9+
10+
// single-file c-header library
11+
{
12+
const exe = b.addExecutable(.{
13+
.name = "single-file-library",
14+
.root_source_file = b.path("main.zig"),
15+
.target = target,
16+
.optimize = optimize,
17+
});
18+
19+
exe.linkLibC();
20+
exe.addIncludePath(b.path("."));
21+
exe.addCSourceFile(.{
22+
.file = b.path("single_file_library.h"),
23+
.lang = .c,
24+
.flags = &.{"-DTSTLIB_IMPLEMENTATION"},
25+
});
26+
27+
test_step.dependOn(&b.addRunArtifact(exe).step);
28+
}
29+
}

test/standalone/c_header/main.zig

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const std = @import("std");
2+
const C = @cImport({
3+
@cInclude("single_file_library.h");
4+
});
5+
6+
pub fn main() !void {
7+
const msg = "hello";
8+
const val = C.tstlib_len(msg);
9+
if (val != msg.len)
10+
std.process.exit(1);
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// library header:
2+
extern unsigned tstlib_len(const char* msg);
3+
4+
// library implementation:
5+
#ifdef TSTLIB_IMPLEMENTATION
6+
7+
#include <string.h>
8+
9+
unsigned tstlib_len(const char* msg)
10+
{
11+
return strlen(msg);
12+
}
13+
14+
#endif

0 commit comments

Comments
 (0)