Skip to content

Commit 0d4b712

Browse files
committed
std.Build: add an option to CSourceFile to override the language detection.
It is normally based on 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 c97db8e commit 0d4b712

File tree

8 files changed

+182
-12
lines changed

8 files changed

+182
-12
lines changed

lib/std/Build/Module.zig

+81-7
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,36 @@ 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 getLangName(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+
lang: ?AsmSourceLang = null,
65+
66+
pub fn dupe(file: AsmSourceFile, b: *std.Build) AsmSourceFile {
67+
return .{
68+
.file = file.file.dupe(b),
69+
.lang = file.lang,
70+
};
71+
}
72+
};
73+
4974
pub const LinkObject = union(enum) {
5075
static_path: LazyPath,
5176
other_step: *Step.Compile,
5277
system_lib: SystemLib,
53-
assembly_file: LazyPath,
78+
assembly_file: *AsmSourceFile,
5479
c_source_file: *CSourceFile,
5580
c_source_files: *CSourceFiles,
5681
win32_resource_file: *RcSourceFile,
@@ -78,21 +103,67 @@ pub const SystemLib = struct {
78103
pub const SearchStrategy = enum { paths_first, mode_first, no_fallback };
79104
};
80105

106+
/// Supported languages for "zig clang -x <lang>".
107+
// subset of Compilation.FileExt
108+
pub const CSourceLang = enum {
109+
/// "c"
110+
c,
111+
/// "c-header"
112+
h,
113+
/// "c++"
114+
cpp,
115+
/// "c++-header"
116+
hpp,
117+
/// "objective-c"
118+
m,
119+
/// "objective-c-header"
120+
hm,
121+
/// "objective-c++"
122+
mm,
123+
/// "objective-c++-header"
124+
hmm,
125+
/// "assembler"
126+
assembly,
127+
/// "assembler-with-cpp"
128+
assembly_with_cpp,
129+
/// "cuda"
130+
cu,
131+
132+
pub fn getLangName(lang: @This()) []const u8 {
133+
return switch (lang) {
134+
.assembly => "assembler",
135+
.assembly_with_cpp => "assembler-with-cpp",
136+
.c => "c",
137+
.cpp => "c++",
138+
.h => "c-header",
139+
.hpp => "c++-header",
140+
.hm => "objective-c-header",
141+
.hmm => "objective-c++-header",
142+
.cu => "cuda",
143+
.m => "objective-c",
144+
.mm => "objective-c++",
145+
};
146+
}
147+
};
148+
81149
pub const CSourceFiles = struct {
82150
root: LazyPath,
83151
/// `files` is relative to `root`, which is
84152
/// the build root by default
85153
files: []const []const u8,
154+
lang: ?CSourceLang = null,
86155
flags: []const []const u8,
87156
};
88157

89158
pub const CSourceFile = struct {
90159
file: LazyPath,
160+
lang: ?CSourceLang = null,
91161
flags: []const []const u8 = &.{},
92162

93163
pub fn dupe(file: CSourceFile, b: *std.Build) CSourceFile {
94164
return .{
95165
.file = file.file.dupe(b),
166+
.lang = file.lang,
96167
.flags = b.dupeStrings(file.flags),
97168
};
98169
}
@@ -286,10 +357,9 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void {
286357
addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree());
287358
},
288359

289-
.static_path,
290-
.assembly_file,
291-
=> |lp| addLazyPathDependencies(m, dependee, lp),
360+
.static_path => |lp| addLazyPathDependencies(m, dependee, lp),
292361

362+
.assembly_file => |x| addLazyPathDependencies(m, dependee, x.file),
293363
.c_source_file => |x| addLazyPathDependencies(m, dependee, x.file),
294364
.win32_resource_file => |x| addLazyPathDependencies(m, dependee, x.file),
295365

@@ -485,6 +555,7 @@ pub const AddCSourceFilesOptions = struct {
485555
/// package that owns the `Compile` step.
486556
root: ?LazyPath = null,
487557
files: []const []const u8,
558+
lang: ?CSourceLang = null,
488559
flags: []const []const u8 = &.{},
489560
};
490561

@@ -506,6 +577,7 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
506577
c_source_files.* = .{
507578
.root = options.root orelse b.path(""),
508579
.files = b.dupeStrings(options.files),
580+
.lang = options.lang,
509581
.flags = b.dupeStrings(options.flags),
510582
};
511583
m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
@@ -541,10 +613,12 @@ pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
541613
}
542614
}
543615

544-
pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
616+
pub fn addAssemblyFile(m: *Module, source: AsmSourceFile) void {
545617
const b = m.owner;
546-
m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
547-
addLazyPathDependenciesOnly(m, source);
618+
const source_file = b.allocator.create(AsmSourceFile) catch @panic("OOM");
619+
source_file.* = source.dupe(b);
620+
m.link_objects.append(b.allocator, .{ .assembly_file = source_file }) catch @panic("OOM");
621+
addLazyPathDependenciesOnly(m, source.file);
548622
}
549623

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

lib/std/Build/Step/Compile.zig

+41-2
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
892892
return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
893893
}
894894

895-
pub fn addAssemblyFile(compile: *Compile, source: LazyPath) void {
895+
pub fn addAssemblyFile(compile: *Compile, source: Module.AsmSourceFile) void {
896896
compile.root_module.addAssemblyFile(source);
897897
}
898898

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

10761076
var prev_has_cflags = false;
10771077
var prev_has_rcflags = false;
1078+
var prev_has_xflag = false;
10781079
var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first;
10791080
var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic;
10801081
// Track the number of positional arguments so that a nice error can be
@@ -1113,6 +1114,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
11131114

11141115
// Inherit dependencies on system libraries and static libraries.
11151116
for (dep.module.link_objects.items) |link_object| {
1117+
if (prev_has_xflag and link_object != .c_source_file and link_object != .c_source_files and link_object != .assembly_file) {
1118+
try zig_args.append("-x");
1119+
try zig_args.append("none");
1120+
prev_has_xflag = false;
1121+
}
1122+
11161123
switch (link_object) {
11171124
.static_path => |static_path| {
11181125
if (my_responsibility) {
@@ -1243,7 +1250,18 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
12431250
try zig_args.append("--");
12441251
prev_has_cflags = false;
12451252
}
1246-
try zig_args.append(asm_file.getPath2(dep.module.owner, step));
1253+
1254+
if (asm_file.lang) |lang| {
1255+
try zig_args.append("-x");
1256+
try zig_args.append(lang.getLangName());
1257+
prev_has_xflag = true;
1258+
} else if (prev_has_xflag) {
1259+
try zig_args.append("-x");
1260+
try zig_args.append("none");
1261+
prev_has_xflag = false;
1262+
}
1263+
1264+
try zig_args.append(asm_file.file.getPath2(dep.module.owner, step));
12471265
total_linker_objects += 1;
12481266
},
12491267

@@ -1264,6 +1282,17 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
12641282
try zig_args.append("--");
12651283
prev_has_cflags = true;
12661284
}
1285+
1286+
if (c_source_file.lang) |lang| {
1287+
try zig_args.append("-x");
1288+
try zig_args.append(lang.getLangName());
1289+
prev_has_xflag = true;
1290+
} else if (prev_has_xflag) {
1291+
try zig_args.append("-x");
1292+
try zig_args.append("none");
1293+
prev_has_xflag = false;
1294+
}
1295+
12671296
try zig_args.append(c_source_file.file.getPath2(dep.module.owner, step));
12681297
total_linker_objects += 1;
12691298
},
@@ -1286,6 +1315,16 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
12861315
prev_has_cflags = true;
12871316
}
12881317

1318+
if (c_source_files.lang) |lang| {
1319+
try zig_args.append("-x");
1320+
try zig_args.append(lang.getLangName());
1321+
prev_has_xflag = true;
1322+
} else if (prev_has_xflag) {
1323+
try zig_args.append("-x");
1324+
try zig_args.append("none");
1325+
prev_has_xflag = false;
1326+
}
1327+
12891328
const root_path = c_source_files.root.getPath2(dep.module.owner, step);
12901329
for (c_source_files.files) |file| {
12911330
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)