Skip to content

Commit

Permalink
std.Build: add an option to CSourceFile to override the language dete…
Browse files Browse the repository at this point in the history
…ction.

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)
  • Loading branch information
xxxbxxx committed Sep 7, 2024
1 parent 5f3d9e0 commit 5fee6bf
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 12 deletions.
88 changes: 81 additions & 7 deletions lib/std/Build/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,36 @@ pub const RPath = union(enum) {
special: []const u8,
};

// subset of Compilation.FileExt
pub const AsmSourceLang = enum {
assembly,
assembly_with_cpp,

pub fn getLangName(lang: @This()) []const u8 {
return switch (lang) {
.assembly => "assembler",
.assembly_with_cpp => "assembler-with-cpp",
};
}
};

pub const AsmSourceFile = struct {
file: LazyPath,
lang: ?AsmSourceLang = null,

pub fn dupe(file: AsmSourceFile, b: *std.Build) AsmSourceFile {
return .{
.file = file.file.dupe(b),
.lang = file.lang,
};
}
};

pub const LinkObject = union(enum) {
static_path: LazyPath,
other_step: *Step.Compile,
system_lib: SystemLib,
assembly_file: LazyPath,
assembly_file: *AsmSourceFile,
c_source_file: *CSourceFile,
c_source_files: *CSourceFiles,
win32_resource_file: *RcSourceFile,
Expand Down Expand Up @@ -78,21 +103,67 @@ pub const SystemLib = struct {
pub const SearchStrategy = enum { paths_first, mode_first, no_fallback };
};

/// Supported languages for "zig clang -x <lang>".
// subset of Compilation.FileExt
pub const CSourceLang = enum {
/// "c"
c,
/// "c-header"
h,
/// "c++"
cpp,
/// "c++-header"
hpp,
/// "objective-c"
m,
/// "objective-c-header"
hm,
/// "objective-c++"
mm,
/// "objective-c++-header"
hmm,
/// "assembler"
assembly,
/// "assembler-with-cpp"
assembly_with_cpp,
/// "cuda"
cu,

pub fn getLangName(lang: @This()) []const u8 {
return switch (lang) {
.assembly => "assembler",
.assembly_with_cpp => "assembler-with-cpp",
.c => "c",
.cpp => "c++",
.h => "c-header",
.hpp => "c++-header",
.hm => "objective-c-header",
.hmm => "objective-c++-header",
.cu => "cuda",
.m => "objective-c",
.mm => "objective-c++",
};
}
};

pub const CSourceFiles = struct {
root: LazyPath,
/// `files` is relative to `root`, which is
/// the build root by default
files: []const []const u8,
lang: ?CSourceLang = null,
flags: []const []const u8,
};

pub const CSourceFile = struct {
file: LazyPath,
lang: ?CSourceLang = null,
flags: []const []const u8 = &.{},

pub fn dupe(file: CSourceFile, b: *std.Build) CSourceFile {
return .{
.file = file.file.dupe(b),
.lang = file.lang,
.flags = b.dupeStrings(file.flags),
};
}
Expand Down Expand Up @@ -286,10 +357,9 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void {
addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree());
},

.static_path,
.assembly_file,
=> |lp| addLazyPathDependencies(m, dependee, lp),
.static_path => |lp| addLazyPathDependencies(m, dependee, lp),

.assembly_file => |x| addLazyPathDependencies(m, dependee, x.file),
.c_source_file => |x| addLazyPathDependencies(m, dependee, x.file),
.win32_resource_file => |x| addLazyPathDependencies(m, dependee, x.file),

Expand Down Expand Up @@ -485,6 +555,7 @@ pub const AddCSourceFilesOptions = struct {
/// package that owns the `Compile` step.
root: ?LazyPath = null,
files: []const []const u8,
lang: ?CSourceLang = null,
flags: []const []const u8 = &.{},
};

Expand All @@ -506,6 +577,7 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
c_source_files.* = .{
.root = options.root orelse b.path(""),
.files = b.dupeStrings(options.files),
.lang = options.lang,
.flags = b.dupeStrings(options.flags),
};
m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
Expand Down Expand Up @@ -541,10 +613,12 @@ pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
}
}

pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
pub fn addAssemblyFile(m: *Module, source: AsmSourceFile) void {
const b = m.owner;
m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
addLazyPathDependenciesOnly(m, source);
const source_file = b.allocator.create(AsmSourceFile) catch @panic("OOM");
source_file.* = source.dupe(b);
m.link_objects.append(b.allocator, .{ .assembly_file = source_file }) catch @panic("OOM");
addLazyPathDependenciesOnly(m, source.file);
}

pub fn addObjectFile(m: *Module, object: LazyPath) void {
Expand Down
43 changes: 41 additions & 2 deletions lib/std/Build/Step/Compile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
}

pub fn addAssemblyFile(compile: *Compile, source: LazyPath) void {
pub fn addAssemblyFile(compile: *Compile, source: Module.AsmSourceFile) void {
compile.root_module.addAssemblyFile(source);
}

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

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

// Inherit dependencies on system libraries and static libraries.
for (dep.module.link_objects.items) |link_object| {
if (prev_has_xflag and link_object != .c_source_file and link_object != .c_source_files and link_object != .assembly_file) {
try zig_args.append("-x");
try zig_args.append("none");
prev_has_xflag = false;
}

switch (link_object) {
.static_path => |static_path| {
if (my_responsibility) {
Expand Down Expand Up @@ -1243,7 +1250,18 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
try zig_args.append("--");
prev_has_cflags = false;
}
try zig_args.append(asm_file.getPath2(dep.module.owner, step));

if (asm_file.lang) |lang| {
try zig_args.append("-x");
try zig_args.append(lang.getLangName());
prev_has_xflag = true;
} else if (prev_has_xflag) {
try zig_args.append("-x");
try zig_args.append("none");
prev_has_xflag = false;
}

try zig_args.append(asm_file.file.getPath2(dep.module.owner, step));
total_linker_objects += 1;
},

Expand All @@ -1264,6 +1282,17 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
try zig_args.append("--");
prev_has_cflags = true;
}

if (c_source_file.lang) |lang| {
try zig_args.append("-x");
try zig_args.append(lang.getLangName());
prev_has_xflag = true;
} else if (prev_has_xflag) {
try zig_args.append("-x");
try zig_args.append("none");
prev_has_xflag = false;
}

try zig_args.append(c_source_file.file.getPath2(dep.module.owner, step));
total_linker_objects += 1;
},
Expand All @@ -1286,6 +1315,16 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
prev_has_cflags = true;
}

if (c_source_files.lang) |lang| {
try zig_args.append("-x");
try zig_args.append(lang.getLangName());
prev_has_xflag = true;
} else if (prev_has_xflag) {
try zig_args.append("-x");
try zig_args.append("none");
prev_has_xflag = false;
}

const root_path = c_source_files.root.getPath2(dep.module.owner, step);
for (c_source_files.files) |file| {
try zig_args.append(b.pathJoin(&.{ root_path, file }));
Expand Down
4 changes: 2 additions & 2 deletions test/link/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ fn addCompileStep(
});
}
if (overlay.asm_source_bytes) |bytes| {
compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes));
compile_step.addAssemblyFile(.{ .file = b.addWriteFiles().add("a.s", bytes) });
}
return compile_step;
}
Expand Down Expand Up @@ -147,7 +147,7 @@ pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
const b = comp.step.owner;
const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
const file = WriteFile.create(b).add("a.s", actual_bytes);
comp.addAssemblyFile(file);
comp.addAssemblyFile(.{ .file = file });
}

pub fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
Expand Down
2 changes: 1 addition & 1 deletion test/src/CompareOutput.zig
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void {
.target = b.graph.host,
.optimize = .Debug,
});
exe.addAssemblyFile(first_file);
exe.addAssemblyFile(.{ .file = first_file });

const run = b.addRunArtifact(exe);
run.setName(annotated_case_name);
Expand Down
3 changes: 3 additions & 0 deletions test/standalone/build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@
.c_compiler = .{
.path = "c_compiler",
},
.c_header = .{
.path = "c_header",
},
.pie = .{
.path = "pie",
},
Expand Down
29 changes: 29 additions & 0 deletions test/standalone/c_header/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

// single-file c-header library
{
const exe = b.addExecutable(.{
.name = "single-file-library",
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});

exe.linkLibC();
exe.addIncludePath(b.path("."));
exe.addCSourceFile(.{
.file = b.path("single_file_library.h"),
.lang = .c,
.flags = &.{"-DTSTLIB_IMPLEMENTATION"},
});

test_step.dependOn(&b.addRunArtifact(exe).step);
}
}
11 changes: 11 additions & 0 deletions test/standalone/c_header/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const std = @import("std");
const C = @cImport({
@cInclude("single_file_library.h");
});

pub fn main() !void {
const msg = "hello";
const val = C.tstlib_len(msg);
if (val != msg.len)
std.process.exit(1);
}
14 changes: 14 additions & 0 deletions test/standalone/c_header/single_file_library.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// library header:
extern unsigned tstlib_len(const char* msg);

// library implementation:
#ifdef TSTLIB_IMPLEMENTATION

#include <string.h>

unsigned tstlib_len(const char* msg)
{
return strlen(msg);
}

#endif

0 comments on commit 5fee6bf

Please sign in to comment.