Skip to content

[clang][driver] Use $ prefix with config file options to have them added after all of the command line options #117573

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions clang/docs/UsersManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,16 @@ In this way, the user may only need to specify a root configuration file with
-L <CFGDIR>/lib
-T <CFGDIR>/ldscripts/link.ld

Usually, config file options are placed before command-line options, regardless
of the actual operation to be performed. The exception is being made for the
options prefixed with the ``$`` character. These will be used only when linker
is being invoked, and added after all of the command-line specified linker
inputs. Here is some example of ``$``-prefixed options:

::
$-Wl,-Bstatic $-lm
$-Wl,-Bshared

Language and Target-Independent Features
========================================

Expand Down
7 changes: 5 additions & 2 deletions clang/include/clang/Driver/Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,11 @@ class Driver {
/// Object that stores strings read from configuration file.
llvm::StringSaver Saver;

/// Arguments originated from configuration file.
std::unique_ptr<llvm::opt::InputArgList> CfgOptions;
/// Arguments originated from configuration file (head part).
std::unique_ptr<llvm::opt::InputArgList> CfgOptionsHead;

/// Arguments originated from configuration file (tail part).
std::unique_ptr<llvm::opt::InputArgList> CfgOptionsTail;

/// Arguments originated from command line.
std::unique_ptr<llvm::opt::InputArgList> CLOptions;
Expand Down
63 changes: 49 additions & 14 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1039,34 +1039,59 @@ bool Driver::readConfigFile(StringRef FileName,
}

// Try reading the given file.
SmallVector<const char *, 32> NewCfgArgs;
if (llvm::Error Err = ExpCtx.readConfigFile(FileName, NewCfgArgs)) {
SmallVector<const char *, 32> NewCfgFileArgs;
if (llvm::Error Err = ExpCtx.readConfigFile(FileName, NewCfgFileArgs)) {
Diag(diag::err_drv_cannot_read_config_file)
<< FileName << toString(std::move(Err));
return true;
}

// Populate head and tail lists. The tail list is used only when linking.
SmallVector<const char *, 32> NewCfgHeadArgs, NewCfgTailArgs;
for (const char *Opt : NewCfgFileArgs) {
// An $-prefixed option should go to the tail list.
if (Opt[0] == '$' && Opt[1])
NewCfgTailArgs.push_back(Opt + 1);
else
NewCfgHeadArgs.push_back(Opt);
}

// Read options from config file.
llvm::SmallString<128> CfgFileName(FileName);
llvm::sys::path::native(CfgFileName);
bool ContainErrors;
auto NewOptions = std::make_unique<InputArgList>(
ParseArgStrings(NewCfgArgs, /*UseDriverMode=*/true, ContainErrors));
bool ContainErrors = false;
auto NewHeadOptions = std::make_unique<InputArgList>(
ParseArgStrings(NewCfgHeadArgs, /*UseDriverMode=*/true, ContainErrors));
if (ContainErrors)
return true;
auto NewTailOptions = std::make_unique<InputArgList>(
ParseArgStrings(NewCfgTailArgs, /*UseDriverMode=*/true, ContainErrors));
if (ContainErrors)
return true;

// Claim all arguments that come from a configuration file so that the driver
// does not warn on any that is unused.
for (Arg *A : *NewOptions)
for (Arg *A : *NewHeadOptions)
A->claim();
for (Arg *A : *NewTailOptions)
A->claim();

if (!CfgOptions)
CfgOptions = std::move(NewOptions);
if (!CfgOptionsHead)
CfgOptionsHead = std::move(NewHeadOptions);
else {
// If this is a subsequent config file, append options to the previous one.
for (auto *Opt : *NewHeadOptions)
appendOneArg(*CfgOptionsHead, Opt);
}

if (!CfgOptionsTail)
CfgOptionsTail = std::move(NewTailOptions);
else {
// If this is a subsequent config file, append options to the previous one.
for (auto *Opt : *NewOptions)
appendOneArg(*CfgOptions, Opt);
for (auto *Opt : *NewTailOptions)
appendOneArg(*CfgOptionsTail, Opt);
}

ConfigFiles.push_back(std::string(CfgFileName));
return false;
}
Expand Down Expand Up @@ -1243,13 +1268,14 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
// Try parsing configuration file.
if (!ContainsError)
ContainsError = loadConfigFiles();
bool HasConfigFile = !ContainsError && (CfgOptions.get() != nullptr);
bool HasConfigFileHead = !ContainsError && CfgOptionsHead;
bool HasConfigFileTail = !ContainsError && CfgOptionsTail;

// All arguments, from both config file and command line.
InputArgList Args = std::move(HasConfigFile ? std::move(*CfgOptions)
: std::move(*CLOptions));
InputArgList Args =
HasConfigFileHead ? std::move(*CfgOptionsHead) : std::move(*CLOptions);

if (HasConfigFile)
if (HasConfigFileHead)
for (auto *Opt : *CLOptions)
if (!Opt->getOption().matches(options::OPT_config))
appendOneArg(Args, Opt);
Expand Down Expand Up @@ -1540,6 +1566,15 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
// Construct the list of inputs.
InputList Inputs;
BuildInputs(C->getDefaultToolChain(), *TranslatedArgs, Inputs);
if (HasConfigFileTail && Inputs.size()) {
Arg *FinalPhaseArg;
if (getFinalPhase(*TranslatedArgs, &FinalPhaseArg) == phases::Link) {
DerivedArgList TranslatedLinkerIns(*CfgOptionsTail);
for (Arg *A : *CfgOptionsTail)
TranslatedLinkerIns.append(A);
BuildInputs(C->getDefaultToolChain(), TranslatedLinkerIns, Inputs);
}
}

// Populate the tool chains for the offloading devices, if any.
CreateOffloadingDeviceToolChains(*C, Inputs);
Expand Down
3 changes: 3 additions & 0 deletions clang/test/Driver/Inputs/config-l.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-Wall $-lm
-Wl,--as-needed $-Wl,-Bstatic
$-lhappy $-Wl,-Bdynamic
26 changes: 26 additions & 0 deletions clang/test/Driver/config-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,29 @@
// CHECK-TWO-CONFIGS: -isysroot
// CHECK-TWO-CONFIGS-SAME: /opt/data
// CHECK-TWO-CONFIGS-SAME: -Wall

//--- The linker input flags should be moved to the end of input list and appear only when linking.
// RUN: %clang --target=aarch64-unknown-linux-gnu --config %S/Inputs/config-l.cfg %s -lmylib -Wl,foo.a -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING
// RUN: %clang --target=aarch64-unknown-linux-gnu --config %S/Inputs/config-l.cfg -fopenmp %s -lmylib -Wl,foo.a -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING-LIBOMP-GOES-AFTER
// RUN: %clang --target=aarch64-unknown-linux-gnu --config %S/Inputs/config-l.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
// RUN: %clang --target=aarch64-unknown-linux-gnu --config %S/Inputs/config-l.cfg -fopenmp -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING-OPENMP
// RUN: %clang --target=x86_64-pc-windows-msvc --config %S/Inputs/config-l.cfg %s -lmylib -Wl,foo.lib -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING-MSVC
// RUN: %clang --target=x86_64-pc-windows-msvc --config %S/Inputs/config-l.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING-MSVC
// CHECK-LINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
// CHECK-LINKING: "-Wall"
// CHECK-LINKING: "--as-needed" "{{.*}}-{{.*}}.o" "-lmylib" "foo.a" "-lm" "-Bstatic" "-lhappy" "-Bdynamic"
// CHECK-LINKING-LIBOMP-GOES-AFTER: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
// CHECK-LINKING-LIBOMP-GOES-AFTER: "-Wall" {{.*}}"-fopenmp"
// CHECK-LINKING-LIBOMP-GOES-AFTER: "--as-needed" "{{.*}}-{{.*}}.o" "-lmylib" "foo.a" "-lm" "-Bstatic" "-lhappy" "-Bdynamic" {{.*}}"-lomp"
// CHECK-NOLINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
// CHECK-NOLINKING: "-Wall"
// CHECK-NOLINKING-NO: "-lm" "-Bstatic" "-lhappy" "-Bdynamic"
// CHECK-NOLINKING-OPENMP: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
// CHECK-NOLINKING-OPENMP: "-Wall" {{.*}}"-fopenmp"
// CHECK-NOLINKING-OPENMP-NO: "-lm" "-Bstatic" "-lhappy" "-Bdynamic" {{.*}}"-lomp"
// CHECK-LINKING-MSVC: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
// CHECK-LINKING-MSVC: "-Wall"
// CHECK-LINKING-MSVC: "--as-needed" "{{.*}}-{{.*}}.o" "mylib.lib" "foo.lib" "m.lib" "-Bstatic" "happy.lib" "-Bdynamic"
// CHECK-NOLINKING-MSVC: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
// CHECK-NOLINKING-MSVC: "-Wall"
// CHECK-NOLINKING-MSVC-NO: "m.lib" "-Bstatic" "happy.lib" "-Bdynamic"
3 changes: 3 additions & 0 deletions flang/test/Driver/Inputs/config-l.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-ffast-math $-lm
-Wl,--as-needed $-Wl,-Bstatic
$-lhappy $-Wl,-Bdynamic
26 changes: 26 additions & 0 deletions flang/test/Driver/config-file.f90
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,29 @@
! CHECK-TWO-CONFIGS-NEXT: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg
! CHECK-TWO-CONFIGS: -ffp-contract=fast
! CHECK-TWO-CONFIGS: -O3

!--- The linker input flags should be moved to the end of input list and appear only when linking.
! RUN: %flang --target=aarch64-unknown-linux-gnu --config %S/Inputs/config-l.cfg %s -lmylib -Wl,foo.a -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING
! RUN: %flang --target=aarch64-unknown-linux-gnu --config %S/Inputs/config-l.cfg -fopenmp %s -lmylib -Wl,foo.a -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING-LIBOMP-GOES-AFTER
! RUN: %flang --target=aarch64-unknown-linux-gnu --config %S/Inputs/config-l.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
! RUN: %flang --target=aarch64-unknown-linux-gnu --config %S/Inputs/config-l.cfg -fopenmp -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING-OPENMP
! RUN: %flang --target=x86_64-pc-windows-msvc --config %S/Inputs/config-l.cfg %s -lmylib -Wl,foo.lib -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING-MSVC
! RUN: %flang --target=x86_64-pc-windows-msvc --config %S/Inputs/config-l.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING-MSVC
! CHECK-LINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
! CHECK-LINKING: "-ffast-math"
! CHECK-LINKING: "--as-needed" "{{.*}}-{{.*}}.o" "-lmylib" "foo.a" "-lm" "-Bstatic" "-lhappy" "-Bdynamic"
! CHECK-LINKING-LIBOMP-GOES-AFTER: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
! CHECK-LINKING-LIBOMP-GOES-AFTER: "-ffast-math" {{.*}}"-fopenmp"
! CHECK-LINKING-LIBOMP-GOES-AFTER: "--as-needed" "{{.*}}-{{.*}}.o" "-lmylib" "foo.a" "-lm" "-Bstatic" "-lhappy" "-Bdynamic" {{.*}}"-lomp"
! CHECK-NOLINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
! CHECK-NOLINKING: "-ffast-math"
! CHECK-NOLINKING-NO: "-lm" "-Bstatic" "-lhappy" "-Bdynamic"
! CHECK-NOLINKING-OPENMP: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
! CHECK-NOLINKING-OPENMP: "-ffast-math" {{.*}}"-fopenmp"
! CHECK-NOLINKING-OPENMP-NO: "-lm" "-Bstatic" "-lhappy" "-Bdynamic" {{.}}"-lomp"
! CHECK-LINKING-MSVC: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
! CHECK-LINKING-MSVC: "-ffast-math"
! CHECK-LINKING-MSVC: "--as-needed" "{{.*}}-{{.*}}.o" "mylib.lib" "foo.lib" "m.lib" "-Bstatic" "happy.lib" "-Bdynamic"
! CHECK-NOLINKING-MSVC: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
! CHECK-NOLINKING-MSVC: "-ffast-math"
! CHECK-NOLINKING-MSVC-NO: "m.lib" "-Bstatic" "happy.lib" "-Bdynamic"
Loading