Skip to content

Commit 0a42851

Browse files
Add support for ${pwd} for -L, -resource-dir and INCLUDE env var (#3832)
1 parent 43ab785 commit 0a42851

File tree

3 files changed

+226
-54
lines changed

3 files changed

+226
-54
lines changed

cargo/private/cargo_build_script.bzl

Lines changed: 84 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -154,86 +154,113 @@ def get_cc_compile_args_and_env(cc_toolchain, feature_configuration):
154154
)
155155
return cc_c_args, cc_cxx_args, cc_env
156156

157-
def _pwd_flags_sysroot(args):
158-
"""Prefix execroot-relative paths of known arguments with ${pwd}.
159-
160-
Args:
161-
args (list): List of tool arguments.
162-
163-
Returns:
164-
list: The modified argument list.
165-
"""
166-
res = []
167-
for arg in args:
168-
s, opt, path = arg.partition("--sysroot=")
169-
if s == "" and not paths.is_absolute(path):
170-
res.append("{}${{pwd}}/{}".format(opt, path))
171-
else:
172-
res.append(arg)
173-
return res
157+
def _prefix_pwd_to_flag(args, flag_variations):
158+
"""Prefix execroot-relative paths for flags that support both concatenated and space-separated forms (unless it ends with `=` or `:`).
174159
175-
def _pwd_flags_isystem(args):
176-
"""Prefix execroot-relative paths of known arguments with ${pwd}.
160+
Handles flags like:
161+
* `-L /path` (space-separated) or `-L/path` (concatenated).
162+
* `-LIBPATH /path` (space-separated) or `-LIBPATH/path` (concatenated) or `-LIBPATH=/path` or `-LIBPATH:/path`
177163
178164
Args:
179165
args (list): List of tool arguments.
166+
flag ([str]): The flag variants to look for (e.g., ["-LIBPATH", "-L"], ["-B"], ["-isystem"], ["-resource-dir"]).
180167
181168
Returns:
182-
list: The modified argument list.
169+
list: The modified argument list with relative paths prefixed with ${pwd}.
183170
"""
184171
res = []
185-
fix_next_arg = False
172+
prefix_next_arg = False
186173
for arg in args:
187-
if fix_next_arg and not paths.is_absolute(arg):
188-
res.append("${{pwd}}/{}".format(arg))
189-
else:
174+
handled = False
175+
new_prefix_next_arg = False
176+
177+
for flag in flag_variations:
178+
# Check for exact match first
179+
if arg == flag:
180+
if flag.endswith("=") or flag.endswith(":"):
181+
# Flag ending with '=' or ':' and empty path: keep as-is
182+
res.append(arg)
183+
handled = True
184+
break
185+
else:
186+
# Flag without '=' or ':': next arg might be space-separated path
187+
new_prefix_next_arg = True
188+
continue
189+
190+
# Check for concatenated form (flag with path)
191+
if arg.startswith(flag):
192+
path = arg[len(flag):]
193+
194+
# Don't prefix absolute paths
195+
if paths.is_absolute(path):
196+
res.append(arg)
197+
else:
198+
res.append("{}${{pwd}}/{}".format(flag, path))
199+
handled = True
200+
break
201+
202+
# Check for space-separated form (only for flags without '=' or ':')
203+
if not flag.endswith("=") and not flag.endswith(":") and prefix_next_arg and not paths.is_absolute(arg):
204+
res.append("${{pwd}}/{}".format(arg))
205+
handled = True
206+
break
207+
208+
if not handled:
190209
res.append(arg)
191210

192-
fix_next_arg = arg == "-isystem"
211+
prefix_next_arg = new_prefix_next_arg
193212

194213
return res
195214

196-
def _pwd_flags_bindir(args):
197-
"""Prefix execroot-relative paths of known arguments with ${pwd}.
215+
def _prefix_pwd_to_paths(args):
216+
"""Prefix execroot-relative paths with ${pwd}.
217+
218+
Handles plain path arguments without any associated flags.
198219
199220
Args:
200-
args (list): List of tool arguments.
221+
args (list): List of path arguments.
201222
202223
Returns:
203-
list: The modified argument list.
224+
list: The modified argument list with relative paths prefixed with ${pwd}.
204225
"""
205226
res = []
206-
fix_next_arg = False
207-
for arg in args:
208-
if fix_next_arg and not paths.is_absolute(arg):
209-
res.append("${{pwd}}/{}".format(arg))
227+
for path in args:
228+
if not paths.is_absolute(path):
229+
res.append("${{pwd}}/{}".format(path))
210230
else:
211-
res.append(arg)
212-
213-
fix_next_arg = arg == "-B"
214-
231+
res.append(path)
215232
return res
216233

234+
def _pwd_flags_sysroot(args):
235+
"""Prefix execroot-relative paths in --sysroot= arguments with ${pwd}."""
236+
return _prefix_pwd_to_flag(args, ["--sysroot="])
237+
217238
def _pwd_flags_fsanitize_ignorelist(args):
218-
"""Prefix execroot-relative paths of known arguments with ${pwd}.
239+
"""Prefix execroot-relative paths in -fsanitize-ignorelist= arguments with ${pwd}."""
240+
return _prefix_pwd_to_flag(args, ["-fsanitize-ignorelist="])
219241

220-
Args:
221-
args (list): List of tool arguments.
242+
def _pwd_flags_isystem(args):
243+
"""Prefix execroot-relative paths in -isystem arguments with ${pwd}."""
244+
return _prefix_pwd_to_flag(args, ["-isystem"])
222245

223-
Returns:
224-
list: The modified argument list.
225-
"""
226-
res = []
227-
for arg in args:
228-
s, opt, path = arg.partition("-fsanitize-ignorelist=")
229-
if s == "" and not paths.is_absolute(path):
230-
res.append("{}${{pwd}}/{}".format(opt, path))
231-
else:
232-
res.append(arg)
233-
return res
246+
def _pwd_flags_L(args):
247+
"""Prefix execroot-relative paths in -L arguments with ${pwd}."""
248+
return _prefix_pwd_to_flag(args, ["-LIBPATH=", "-LIBPATH:", "-LIBPATH", "-L"])
249+
250+
def _pwd_flags_B(args):
251+
"""Prefix execroot-relative paths in -B arguments with ${pwd}."""
252+
return _prefix_pwd_to_flag(args, ["-B"])
253+
254+
def _pwd_flags_resource_dir(args):
255+
"""Prefix execroot-relative paths in -resource-dir arguments with ${pwd}."""
256+
return _prefix_pwd_to_flag(args, ["-resource-dir=", "-resource-dir"])
257+
258+
def _pwd_paths(args):
259+
"""Prefix execroot-relative paths with ${pwd}."""
260+
return _prefix_pwd_to_paths(args)
234261

235262
def _pwd_flags(args):
236-
return _pwd_flags_fsanitize_ignorelist(_pwd_flags_isystem(_pwd_flags_bindir(_pwd_flags_sysroot(args))))
263+
return _pwd_flags_fsanitize_ignorelist(_pwd_flags_isystem(_pwd_flags_L(_pwd_flags_B(_pwd_flags_resource_dir(_pwd_flags_sysroot(args))))))
237264

238265
def _feature_enabled(ctx, feature_name, default = False):
239266
"""Check if a feature is enabled.
@@ -442,7 +469,11 @@ def _cargo_build_script_impl(ctx):
442469
cc_c_args, cc_cxx_args, cc_env = get_cc_compile_args_and_env(cc_toolchain, feature_configuration)
443470
include = cc_env.get("INCLUDE")
444471
if include:
445-
env["INCLUDE"] = include
472+
if toolchain.exec_triple.str.find("windows") > 0:
473+
path_separator = ";"
474+
else:
475+
path_separator = ":"
476+
env["INCLUDE"] = path_separator.join(_pwd_paths(include.split(path_separator)))
446477

447478
toolchain_tools.append(cc_toolchain.all_files)
448479

test/cargo_build_script/cc_args_and_env/BUILD.bazel

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@ load(
44
"bindir_relative_test",
55
"fsanitize_ignorelist_absolute_test",
66
"fsanitize_ignorelist_relative_test",
7+
"include_absolute_test",
8+
"include_mixed_test",
9+
"include_relative_test",
710
"isystem_absolute_test",
811
"isystem_relative_test",
912
"legacy_cc_toolchain_test",
13+
"libpath_absolute_test",
14+
"libpath_relative_test",
15+
"resource_dir_absolute_test",
16+
"resource_dir_relative_test",
1017
"sysroot_absolute_test",
1118
"sysroot_next_absolute_test",
1219
"sysroot_relative_test",
@@ -31,3 +38,17 @@ fsanitize_ignorelist_absolute_test(name = "fsanitize_ignorelist_absolute_test")
3138
fsanitize_ignorelist_relative_test(name = "fsanitize_ignorelist_relative_test")
3239

3340
legacy_cc_toolchain_test(name = "legacy_cc_toolchain_test")
41+
42+
libpath_absolute_test(name = "libpath_absolute_test")
43+
44+
libpath_relative_test(name = "libpath_relative_test")
45+
46+
resource_dir_absolute_test(name = "resource_dir_absolute_test")
47+
48+
resource_dir_relative_test(name = "resource_dir_relative_test")
49+
50+
include_relative_test(name = "include_relative_test")
51+
52+
include_absolute_test(name = "include_absolute_test")
53+
54+
include_mixed_test(name = "include_mixed_test")

test/cargo_build_script/cc_args_and_env/cc_args_and_env_test.bzl

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ To verify the processed cargo cc_args, we use cc_args_and_env_analysis_test().
99

1010
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
1111
load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES", "ACTION_NAME_GROUPS")
12-
load("@rules_cc//cc:cc_toolchain_config_lib.bzl", "action_config", "feature", "flag_group", "flag_set", "tool", "tool_path")
12+
load("@rules_cc//cc:cc_toolchain_config_lib.bzl", "action_config", "env_entry", "env_set", "feature", "flag_group", "flag_set", "tool", "tool_path")
1313
load("@rules_cc//cc:defs.bzl", "cc_toolchain")
1414
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
1515
load("@rules_cc//cc/toolchains:cc_toolchain_config_info.bzl", "CcToolchainConfigInfo")
@@ -36,6 +36,17 @@ def _test_cc_config_impl(ctx):
3636
]),
3737
),
3838
],
39+
env_sets = [
40+
env_set(
41+
actions = ACTION_NAME_GROUPS.all_cc_compile_actions,
42+
env_entries = [
43+
env_entry(
44+
key = "INCLUDE",
45+
value = ctx.attr.extra_include_paths,
46+
),
47+
],
48+
),
49+
] if ctx.attr.extra_include_paths else [],
3950
),
4051
feature(
4152
name = "default_cpp_flags",
@@ -156,6 +167,7 @@ test_cc_config = rule(
156167
"extra_ar_flags": attr.string_list(),
157168
"extra_cc_compile_flags": attr.string_list(),
158169
"extra_cxx_compile_flags": attr.string_list(),
170+
"extra_include_paths": attr.string(default = ""),
159171
"legacy_cc_toolchain": attr.bool(default = False),
160172
},
161173
provides = [CcToolchainConfigInfo],
@@ -241,6 +253,15 @@ def _cc_args_and_env_analysis_test_impl(ctx):
241253
"set up by most Bazel C/C++ toolchains is extremely error-prone.",
242254
)
243255

256+
if ctx.attr.expected_include:
257+
actual_include = cargo_action.env.get("INCLUDE")
258+
asserts.equals(
259+
env,
260+
ctx.attr.expected_include,
261+
actual_include,
262+
"error: expected INCLUDE '{}' but got '{}'".format(ctx.attr.expected_include, actual_include),
263+
)
264+
244265
return analysistest.end(env)
245266

246267
cc_args_and_env_analysis_test = analysistest.make(
@@ -249,6 +270,7 @@ cc_args_and_env_analysis_test = analysistest.make(
249270
attrs = {
250271
"expected_cflags": attr.string_list(default = ["-Wall"]),
251272
"expected_cxxflags": attr.string_list(default = ["-fno-rtti"]),
273+
"expected_include": attr.string(default = ""),
252274
"legacy_cc_toolchain": attr.bool(default = False),
253275
},
254276
)
@@ -259,6 +281,7 @@ def cargo_build_script_with_extra_cc_compile_flags(
259281
extra_cc_compile_flags = ["-Wall"],
260282
extra_cxx_compile_flags = ["-fno-rtti"],
261283
extra_ar_flags = ["-x"],
284+
extra_include_paths = "",
262285
legacy_cc_toolchain = False):
263286
"""Produces a test cargo_build_script target that's set up to use a custom cc_toolchain with the extra_cc_compile_flags.
264287
@@ -273,6 +296,7 @@ def cargo_build_script_with_extra_cc_compile_flags(
273296
extra_cc_compile_flags: Extra C/C++ args for the cc_toolchain.
274297
extra_cxx_compile_flags: Extra C++-specific args for the cc_toolchain.
275298
extra_ar_flags: Extra archiver args for the cc_toolchain.
299+
extra_include_paths: INCLUDE environment variable value for the cc_toolchain.
276300
legacy_cc_toolchain: Enables legacy tool_path configuration of the cc
277301
cc toolchain.
278302
"""
@@ -282,6 +306,7 @@ def cargo_build_script_with_extra_cc_compile_flags(
282306
extra_cc_compile_flags = extra_cc_compile_flags,
283307
extra_cxx_compile_flags = extra_cxx_compile_flags,
284308
extra_ar_flags = extra_ar_flags,
309+
extra_include_paths = extra_include_paths,
285310
legacy_cc_toolchain = legacy_cc_toolchain,
286311
)
287312
cc_toolchain(
@@ -423,3 +448,98 @@ def legacy_cc_toolchain_test(name):
423448
target_under_test = "%s/cargo_build_script" % name,
424449
legacy_cc_toolchain = True,
425450
)
451+
452+
def libpath_relative_test(name):
453+
cargo_build_script_with_extra_cc_compile_flags(
454+
name = "%s/cargo_build_script" % name,
455+
extra_cc_compile_flags = ["-Ltest/relative/sysroot", "-L", "test/relative/sysroot2", "-LIBPATH:test/relative/sysroot3", "-LIBPATH=test/relative/sysroot4", "-LIBPATH:", "some_unrelated_arg", "-LIBPATH=", "some_unrelated_arg2"],
456+
)
457+
cc_args_and_env_analysis_test(
458+
name = name,
459+
target_under_test = "%s/cargo_build_script" % name,
460+
expected_cflags = ["-L${pwd}/test/relative/sysroot", "-L", "${pwd}/test/relative/sysroot2", "-LIBPATH:${pwd}/test/relative/sysroot3", "-LIBPATH=${pwd}/test/relative/sysroot4", "-LIBPATH:", "some_unrelated_arg", "-LIBPATH=", "some_unrelated_arg2"],
461+
)
462+
463+
def libpath_absolute_test(name):
464+
cargo_build_script_with_extra_cc_compile_flags(
465+
name = "%s/cargo_build_script" % name,
466+
extra_cc_compile_flags = ["-L/test/absolute/sysroot", "-L", "/test/absolute/sysroot2", "-LIBPATH:/test/absolute/sysroot3", "-LIBPATH=/test/absolute/sysroot4", "-LIBPATH:", "some_unrelated_arg", "-LIBPATH=", "some_unrelated_arg2"],
467+
)
468+
cc_args_and_env_analysis_test(
469+
name = name,
470+
target_under_test = "%s/cargo_build_script" % name,
471+
expected_cflags = ["-L/test/absolute/sysroot", "-L", "/test/absolute/sysroot2", "-LIBPATH:/test/absolute/sysroot3", "-LIBPATH=/test/absolute/sysroot4", "-LIBPATH:", "some_unrelated_arg", "-LIBPATH=", "some_unrelated_arg2"],
472+
)
473+
474+
def resource_dir_relative_test(name):
475+
cargo_build_script_with_extra_cc_compile_flags(
476+
name = "%s/cargo_build_script" % name,
477+
extra_cc_compile_flags = ["-resource-dir", "test/relative/resources", "-resource-dir=test/relative/resources2", "-resource-dir=", "some_unrelated_arg"],
478+
)
479+
cc_args_and_env_analysis_test(
480+
name = name,
481+
target_under_test = "%s/cargo_build_script" % name,
482+
expected_cflags = ["-resource-dir", "${pwd}/test/relative/resources", "-resource-dir=${pwd}/test/relative/resources2", "-resource-dir=", "some_unrelated_arg"],
483+
)
484+
485+
def resource_dir_absolute_test(name):
486+
cargo_build_script_with_extra_cc_compile_flags(
487+
name = "%s/cargo_build_script" % name,
488+
extra_cc_compile_flags = ["-resource-dir", "/test/absolute/resources", "-resource-dir=/test/absolute/resources2", "-resource-dir=", "some_unrelated_arg"],
489+
)
490+
cc_args_and_env_analysis_test(
491+
name = name,
492+
target_under_test = "%s/cargo_build_script" % name,
493+
expected_cflags = ["-resource-dir", "/test/absolute/resources", "-resource-dir=/test/absolute/resources2", "-resource-dir=", "some_unrelated_arg"],
494+
)
495+
496+
def include_relative_test(name):
497+
cargo_build_script_with_extra_cc_compile_flags(
498+
name = "%s/cargo_build_script" % name,
499+
extra_include_paths = select({
500+
"@platforms//os:windows": "test/relative/include;another/relative/path",
501+
"//conditions:default": "test/relative/include:another/relative/path",
502+
}),
503+
)
504+
cc_args_and_env_analysis_test(
505+
name = name,
506+
target_under_test = "%s/cargo_build_script" % name,
507+
expected_include = select({
508+
"@platforms//os:windows": "${pwd}/test/relative/include;${pwd}/another/relative/path",
509+
"//conditions:default": "${pwd}/test/relative/include:${pwd}/another/relative/path",
510+
}),
511+
)
512+
513+
def include_absolute_test(name):
514+
cargo_build_script_with_extra_cc_compile_flags(
515+
name = "%s/cargo_build_script" % name,
516+
extra_include_paths = select({
517+
"@platforms//os:windows": "/test/absolute/include;/another/absolute/path",
518+
"//conditions:default": "/test/absolute/include:/another/absolute/path",
519+
}),
520+
)
521+
cc_args_and_env_analysis_test(
522+
name = name,
523+
target_under_test = "%s/cargo_build_script" % name,
524+
expected_include = select({
525+
"@platforms//os:windows": "/test/absolute/include;/another/absolute/path",
526+
"//conditions:default": "/test/absolute/include:/another/absolute/path",
527+
}),
528+
)
529+
530+
def include_mixed_test(name):
531+
cargo_build_script_with_extra_cc_compile_flags(
532+
name = "%s/cargo_build_script" % name,
533+
extra_include_paths = select({
534+
"@platforms//os:windows": "/test/absolute/include;test/relative/path",
535+
"//conditions:default": "/test/absolute/include:test/relative/path",
536+
}),
537+
)
538+
cc_args_and_env_analysis_test(
539+
name = name,
540+
target_under_test = "%s/cargo_build_script" % name,
541+
expected_include = select({
542+
"@platforms//os:windows": "/test/absolute/include;${pwd}/test/relative/path",
543+
"//conditions:default": "/test/absolute/include:${pwd}/test/relative/path",
544+
}),
545+
)

0 commit comments

Comments
 (0)