Skip to content

Commit 0f87959

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 e2e3633 commit 0f87959

File tree

11 files changed

+203
-20
lines changed

11 files changed

+203
-20
lines changed

lib/compiler/build_runner.zig

+2-3
Original file line numberDiff line numberDiff line change
@@ -1504,9 +1504,8 @@ fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
15041504
.special => {},
15051505
};
15061506
for (mod.link_objects.items) |link_object| switch (link_object) {
1507-
.static_path,
1508-
.assembly_file,
1509-
=> |lp| lp.addStepDependencies(step),
1507+
.static_path => |lp| lp.addStepDependencies(step),
1508+
.assembly_file => |source| source.file.addStepDependencies(step),
15101509
.other_step => |other| step.dependOn(&other.step),
15111510
.system_lib => {},
15121511
.c_source_file => |source| source.file.addStepDependencies(step),

lib/std/Build/Module.zig

+82-3
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
}
@@ -377,6 +452,7 @@ pub const AddCSourceFilesOptions = struct {
377452
/// package that owns the `Compile` step.
378453
root: ?LazyPath = null,
379454
files: []const []const u8,
455+
lang: ?CSourceLang = null,
380456
flags: []const []const u8 = &.{},
381457
};
382458

@@ -398,6 +474,7 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
398474
c_source_files.* = .{
399475
.root = options.root orelse b.path(""),
400476
.files = b.dupeStrings(options.files),
477+
.lang = options.lang,
401478
.flags = b.dupeStrings(options.flags),
402479
};
403480
m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
@@ -427,9 +504,11 @@ pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
427504
m.link_objects.append(allocator, .{ .win32_resource_file = rc_source_file }) catch @panic("OOM");
428505
}
429506

430-
pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
507+
pub fn addAssemblyFile(m: *Module, source: AsmSourceFile) void {
431508
const b = m.owner;
432-
m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
509+
const source_file = b.allocator.create(AsmSourceFile) catch @panic("OOM");
510+
source_file.* = source.dupe(b);
511+
m.link_objects.append(b.allocator, .{ .assembly_file = source_file }) catch @panic("OOM");
433512
}
434513

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

lib/std/Build/Step/Compile.zig

+49-4
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,7 @@ pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
889889
return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
890890
}
891891

892-
pub fn addAssemblyFile(compile: *Compile, source: LazyPath) void {
892+
pub fn addAssemblyFile(compile: *Compile, source: Module.AsmSourceFile) void {
893893
compile.root_module.addAssemblyFile(source);
894894
}
895895

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

10731073
var prev_has_cflags = false;
10741074
var prev_has_rcflags = false;
1075+
var prev_has_xflag = false;
10751076
var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first;
10761077
var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic;
10771078
// Track the number of positional arguments so that a nice error can be
@@ -1108,6 +1109,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
11081109

11091110
// Inherit dependencies on system libraries and static libraries.
11101111
for (mod.link_objects.items) |link_object| {
1112+
if (prev_has_xflag and link_object != .c_source_file and link_object != .c_source_files and link_object != .assembly_file) {
1113+
try zig_args.append("-x");
1114+
try zig_args.append("none");
1115+
prev_has_xflag = false;
1116+
}
1117+
11111118
switch (link_object) {
11121119
.static_path => |static_path| {
11131120
if (my_responsibility) {
@@ -1233,12 +1240,31 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
12331240
.assembly_file => |asm_file| l: {
12341241
if (!my_responsibility) break :l;
12351242

1236-
if (prev_has_cflags) {
1243+
if (asm_file.flags.len == 0) {
1244+
if (prev_has_cflags) {
1245+
try zig_args.append("-cflags");
1246+
try zig_args.append("--");
1247+
prev_has_cflags = false;
1248+
}
1249+
} else {
12371250
try zig_args.append("-cflags");
1251+
for (asm_file.flags) |arg| {
1252+
try zig_args.append(arg);
1253+
}
12381254
try zig_args.append("--");
1239-
prev_has_cflags = false;
1255+
prev_has_cflags = true;
1256+
}
1257+
if (asm_file.lang) |lang| {
1258+
try zig_args.append("-x");
1259+
try zig_args.append(lang.getName());
1260+
prev_has_xflag = true;
1261+
} else if (prev_has_xflag) {
1262+
try zig_args.append("-x");
1263+
try zig_args.append("none");
1264+
prev_has_xflag = false;
12401265
}
1241-
try zig_args.append(asm_file.getPath2(mod.owner, step));
1266+
1267+
try zig_args.append(asm_file.file.getPath2(mod.owner, step));
12421268
total_linker_objects += 1;
12431269
},
12441270

@@ -1259,6 +1285,16 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
12591285
try zig_args.append("--");
12601286
prev_has_cflags = true;
12611287
}
1288+
if (c_source_file.lang) |lang| {
1289+
try zig_args.append("-x");
1290+
try zig_args.append(lang.getName());
1291+
prev_has_xflag = true;
1292+
} else if (prev_has_xflag) {
1293+
try zig_args.append("-x");
1294+
try zig_args.append("none");
1295+
prev_has_xflag = false;
1296+
}
1297+
12621298
try zig_args.append(c_source_file.file.getPath2(mod.owner, step));
12631299
total_linker_objects += 1;
12641300
},
@@ -1280,6 +1316,15 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
12801316
try zig_args.append("--");
12811317
prev_has_cflags = true;
12821318
}
1319+
if (c_source_files.lang) |lang| {
1320+
try zig_args.append("-x");
1321+
try zig_args.append(lang.getName());
1322+
prev_has_xflag = true;
1323+
} else if (prev_has_xflag) {
1324+
try zig_args.append("-x");
1325+
try zig_args.append("none");
1326+
prev_has_xflag = false;
1327+
}
12831328

12841329
const root_path = c_source_files.root.getPath2(mod.owner, step);
12851330
for (c_source_files.files) |file| {

test/link/elf.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -2918,7 +2918,7 @@ fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
29182918
addAsmSourceBytes(dso,
29192919
\\.globl foo
29202920
\\foo = 3;
2921-
);
2921+
, &.{});
29222922

29232923
const obj = addObject(b, opts, .{
29242924
.name = "obj",
@@ -3533,7 +3533,7 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
35333533
\\.section .tcommon,"awT",@nobits
35343534
\\y:
35353535
\\.zero 1024
3536-
);
3536+
, &.{});
35373537
addCSourceBytes(exe,
35383538
\\#include <stdio.h>
35393539
\\extern _Thread_local char x[1024000];

test/link/link.zig

+8-5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step {
3333
const OverlayOptions = struct {
3434
name: []const u8,
3535
asm_source_bytes: ?[]const u8 = null,
36+
asm_source_flags: []const []const u8 = &.{},
3637
c_source_bytes: ?[]const u8 = null,
3738
c_source_flags: []const []const u8 = &.{},
3839
cpp_source_bytes: ?[]const u8 = null,
@@ -122,7 +123,10 @@ fn createModule(b: *Build, base: Options, overlay: OverlayOptions) *Build.Module
122123
});
123124
}
124125
if (overlay.asm_source_bytes) |bytes| {
125-
mod.addAssemblyFile(write_files.add("a.s", bytes));
126+
mod.addAssemblyFile(.{
127+
.file = write_files.add("a.s", bytes),
128+
.flags = overlay.asm_source_flags,
129+
});
126130
}
127131

128132
return mod;
@@ -147,11 +151,10 @@ pub fn addCppSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []con
147151
comp.addCSourceFile(.{ .file = file, .flags = flags });
148152
}
149153

150-
pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
154+
pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
151155
const b = comp.step.owner;
152-
const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
153-
const file = WriteFile.create(b).add("a.s", actual_bytes);
154-
comp.addAssemblyFile(file);
156+
const file = WriteFile.create(b).add("a.s", bytes);
157+
comp.addAssemblyFile(.{ .file = file, .flags = flags });
155158
}
156159

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

test/link/macho.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -630,13 +630,13 @@ fn testHeaderWeakFlags(b: *Build, opts: Options) *Step {
630630
\\_main:
631631
\\ bl _x
632632
\\ ret
633-
),
633+
, &.{}),
634634
.x86_64 => addAsmSourceBytes(obj,
635635
\\.globl _main
636636
\\_main:
637637
\\ callq _x
638638
\\ ret
639-
),
639+
, &.{}),
640640
else => unreachable,
641641
}
642642

test/src/CompareOutput.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void {
104104
.optimize = .Debug,
105105
}),
106106
});
107-
exe.root_module.addAssemblyFile(first_file);
107+
exe.root_module.addAssemblyFile(.{ .file = first_file });
108108

109109
const run = b.addRunArtifact(exe);
110110
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)