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

Conversation

pawosm-arm
Copy link
Contributor

@pawosm-arm pawosm-arm commented Nov 25, 2024

Currently, if a -l (or -Wl,) flag is added into a config file
(e.g. clang.cfg), it is situated before any object file in the
effective command line. If the library requested by given -l flag is
static, its symbols will not be made visible to any of the object
files provided by the user. Also, the presence of any of the linker
flags in a config file confuses the driver whenever the user invokes
clang without any parameters (see issue #67209).

This patch attempts to solve both of the problems, by allowing a split
of the arguments list into two parts. The head part of the list will
be used as before, but the tail part will be appended after the
command line flags provided by the user and only when it is known
that the linking should occur. The $-prefixed arguments will be added
to the tail part.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' flang:driver flang Flang issues not falling into any other category labels Nov 25, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 25, 2024

@llvm/pr-subscribers-flang-driver

@llvm/pr-subscribers-clang

Author: Paul Osmialowski (pawosm-arm)

Changes

Currently, if a -l flag is added into a config file (e.g. clang.cfg), it is situated before any object file in the effective command line. If the library requested by given -l flag is static, its symbols will not be made visible to any of the object files provided by the user. Also, the presence of any of the -l flags in a config file confuses the driver whenever the user invokes clang without any parameters.

This patch solves both of those problems, by moving the -lm flags specified in a config file to the end of the input list, and by discarding them when there are no other inputs provided by the user, or the last phase is not the linking phase.


Full diff: https://github.com/llvm/llvm-project/pull/117573.diff

8 Files Affected:

  • (modified) clang/include/clang/Driver/Driver.h (+3)
  • (modified) clang/lib/Driver/Driver.cpp (+32)
  • (added) clang/test/Driver/Inputs/config-l-openmp.cfg (+1)
  • (added) clang/test/Driver/Inputs/config-l.cfg (+1)
  • (modified) clang/test/Driver/config-file.c (+19)
  • (added) flang/test/Driver/Inputs/config-l-openmp.cfg (+1)
  • (added) flang/test/Driver/Inputs/config-l.cfg (+1)
  • (modified) flang/test/Driver/config-file.f90 (+19)
diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h
index 9177d56718ee77..b21606b6f54b77 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -297,6 +297,9 @@ class Driver {
   /// Object that stores strings read from configuration file.
   llvm::StringSaver Saver;
 
+  /// Linker inputs originated from configuration file.
+  std::unique_ptr<llvm::opt::InputArgList> CfgLinkerInputs;
+
   /// Arguments originated from configuration file.
   std::unique_ptr<llvm::opt::InputArgList> CfgOptions;
 
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index ad14b5c9b6dc80..bfd440461ea601 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1062,6 +1062,15 @@ bool Driver::readConfigFile(StringRef FileName,
   for (Arg *A : *NewOptions)
     A->claim();
 
+  std::unique_ptr<InputArgList> NewLinkerIns = std::make_unique<InputArgList>();
+  for (Arg *A : NewOptions->filtered(options::OPT_l)) {
+    const Arg *BaseArg = &A->getBaseArg();
+    if (BaseArg == A)
+      BaseArg = nullptr;
+    appendOneArg(*NewLinkerIns, A, BaseArg);
+  }
+  NewOptions->eraseArg(options::OPT_l);
+
   if (!CfgOptions)
     CfgOptions = std::move(NewOptions);
   else {
@@ -1073,6 +1082,19 @@ bool Driver::readConfigFile(StringRef FileName,
       appendOneArg(*CfgOptions, Opt, BaseArg);
     }
   }
+
+  if (!CfgLinkerInputs)
+    CfgLinkerInputs = std::move(NewLinkerIns);
+  else {
+    // If this is a subsequent config file, append options to the previous one.
+    for (auto *Opt : *NewLinkerIns) {
+      const Arg *BaseArg = &Opt->getBaseArg();
+      if (BaseArg == Opt)
+        BaseArg = nullptr;
+      appendOneArg(*CfgLinkerInputs, Opt, BaseArg);
+    }
+  }
+
   ConfigFiles.push_back(std::string(CfgFileName));
   return false;
 }
@@ -1250,6 +1272,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   if (!ContainsError)
     ContainsError = loadConfigFiles();
   bool HasConfigFile = !ContainsError && (CfgOptions.get() != nullptr);
+  bool HasConfigLinkerIn = !ContainsError && (CfgLinkerInputs.get() != nullptr);
 
   // All arguments, from both config file and command line.
   InputArgList Args = std::move(HasConfigFile ? std::move(*CfgOptions)
@@ -1552,6 +1575,15 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   // Construct the list of inputs.
   InputList Inputs;
   BuildInputs(C->getDefaultToolChain(), *TranslatedArgs, Inputs);
+  if (HasConfigLinkerIn && Inputs.size()) {
+    Arg *FinalPhaseArg;
+    if (getFinalPhase(*TranslatedArgs, &FinalPhaseArg) == phases::Link) {
+      DerivedArgList TranslatedLinkerIns(*CfgLinkerInputs);
+      for (Arg *A : *CfgLinkerInputs)
+        TranslatedLinkerIns.append(A);
+      BuildInputs(C->getDefaultToolChain(), TranslatedLinkerIns, Inputs);
+    }
+  }
 
   // Populate the tool chains for the offloading devices, if any.
   CreateOffloadingDeviceToolChains(*C, Inputs);
diff --git a/clang/test/Driver/Inputs/config-l-openmp.cfg b/clang/test/Driver/Inputs/config-l-openmp.cfg
new file mode 100644
index 00000000000000..91f8884b6e9e23
--- /dev/null
+++ b/clang/test/Driver/Inputs/config-l-openmp.cfg
@@ -0,0 +1 @@
+-Wall -fopenmp -lm -lhappy
diff --git a/clang/test/Driver/Inputs/config-l.cfg b/clang/test/Driver/Inputs/config-l.cfg
new file mode 100644
index 00000000000000..0ebc28baddbe2c
--- /dev/null
+++ b/clang/test/Driver/Inputs/config-l.cfg
@@ -0,0 +1 @@
+-Wall -lm -lhappy
diff --git a/clang/test/Driver/config-file.c b/clang/test/Driver/config-file.c
index 9df830ca4c7538..799e545e1e518b 100644
--- a/clang/test/Driver/config-file.c
+++ b/clang/test/Driver/config-file.c
@@ -82,3 +82,22 @@
 // CHECK-TWO-CONFIGS: -isysroot
 // CHECK-TWO-CONFIGS-SAME: /opt/data
 // CHECK-TWO-CONFIGS-SAME: -Wall
+
+//--- The -l flags should be moved to the end of input list and appear only when linking.
+// RUN: %clang --config %S/Inputs/config-l.cfg -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING
+// RUN: %clang --config %S/Inputs/config-l-openmp.cfg -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING-LIBOMP-GOES-LAST
+// RUN: %clang --config %S/Inputs/config-l.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
+// RUN: %clang --config %S/Inputs/config-l-openmp.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
+// CHECK-LINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
+// CHECK-LINKING: "-Wall"
+// CHECK-LINKING: "/tmp/{{.*}}-{{.*}}.o" "-lm" "-lhappy"
+// CHECK-LINKING-LIBOMP-GOES-LAST: Configuration file: {{.*}}Inputs{{.}}config-l-openmp.cfg
+// CHECK-LINKING-LIBOMP-GOES-LAST: "-Wall"
+// CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-fopenmp"
+// CHECK-LINKING-LIBOMP-GOES-LAST: "/tmp/{{.*}}-{{.*}}.o" "-lm" "-lhappy"
+// CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-lomp"
+// CHECK-NOLINKING: Configuration file: {{.*}}Inputs{{.}}config-l{{.*}}.cfg
+// CHECK-NOLINKING: "-Wall"
+// CHECK-NOLINKING-NO: "-lm"
+// CHECK-NOLINKING-NO: "-lhappy"
+// CHECK-NOLINKING-NO: "-lomp"
diff --git a/flang/test/Driver/Inputs/config-l-openmp.cfg b/flang/test/Driver/Inputs/config-l-openmp.cfg
new file mode 100644
index 00000000000000..46ab3273f84f19
--- /dev/null
+++ b/flang/test/Driver/Inputs/config-l-openmp.cfg
@@ -0,0 +1 @@
+-ffast-math -fopenmp -lm -lhappy
diff --git a/flang/test/Driver/Inputs/config-l.cfg b/flang/test/Driver/Inputs/config-l.cfg
new file mode 100644
index 00000000000000..af2d0a99475a2a
--- /dev/null
+++ b/flang/test/Driver/Inputs/config-l.cfg
@@ -0,0 +1 @@
+-ffast-math -lm -lhappy
diff --git a/flang/test/Driver/config-file.f90 b/flang/test/Driver/config-file.f90
index 70316dd971f36b..15b18f4420e6b3 100644
--- a/flang/test/Driver/config-file.f90
+++ b/flang/test/Driver/config-file.f90
@@ -61,3 +61,22 @@
 ! CHECK-TWO-CONFIGS-NEXT: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg
 ! CHECK-TWO-CONFIGS: -ffp-contract=fast
 ! CHECK-TWO-CONFIGS: -O3
+
+!--- The -l flags should be moved to the end of input list and appear only when linking.
+! RUN: %flang --config %S/Inputs/config-l.cfg -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING
+! RUN: %flang --config %S/Inputs/config-l-openmp.cfg -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING-LIBOMP-GOES-LAST
+! RUN: %flang --config %S/Inputs/config-l.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
+! RUN: %flang --config %S/Inputs/config-l-openmp.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
+! CHECK-LINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
+! CHECK-LINKING: "-ffast-math"
+! CHECK-LINKING: "/tmp/{{.*}}-{{.*}}.o" "-lm" "-lhappy"
+! CHECK-LINKING-LIBOMP-GOES-LAST: Configuration file: {{.*}}Inputs{{.}}config-l-openmp.cfg
+! CHECK-LINKING-LIBOMP-GOES-LAST: "-ffast-math"
+! CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-fopenmp"
+! CHECK-LINKING-LIBOMP-GOES-LAST: "/tmp/{{.*}}-{{.*}}.o" "-lm" "-lhappy"
+! CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-lomp"
+! CHECK-NOLINKING: Configuration file: {{.*}}Inputs{{.}}config-l{{.*}}.cfg
+! CHECK-NOLINKING: "-ffast-math"
+! CHECK-NOLINKING-NO: "-lm"
+! CHECK-NOLINKING-NO: "-lhappy"
+! CHECK-NOLINKING-NO: "-lomp"

@llvmbot
Copy link
Member

llvmbot commented Nov 25, 2024

@llvm/pr-subscribers-clang-driver

Author: Paul Osmialowski (pawosm-arm)

Changes

Currently, if a -l flag is added into a config file (e.g. clang.cfg), it is situated before any object file in the effective command line. If the library requested by given -l flag is static, its symbols will not be made visible to any of the object files provided by the user. Also, the presence of any of the -l flags in a config file confuses the driver whenever the user invokes clang without any parameters.

This patch solves both of those problems, by moving the -lm flags specified in a config file to the end of the input list, and by discarding them when there are no other inputs provided by the user, or the last phase is not the linking phase.


Full diff: https://github.com/llvm/llvm-project/pull/117573.diff

8 Files Affected:

  • (modified) clang/include/clang/Driver/Driver.h (+3)
  • (modified) clang/lib/Driver/Driver.cpp (+32)
  • (added) clang/test/Driver/Inputs/config-l-openmp.cfg (+1)
  • (added) clang/test/Driver/Inputs/config-l.cfg (+1)
  • (modified) clang/test/Driver/config-file.c (+19)
  • (added) flang/test/Driver/Inputs/config-l-openmp.cfg (+1)
  • (added) flang/test/Driver/Inputs/config-l.cfg (+1)
  • (modified) flang/test/Driver/config-file.f90 (+19)
diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h
index 9177d56718ee77..b21606b6f54b77 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -297,6 +297,9 @@ class Driver {
   /// Object that stores strings read from configuration file.
   llvm::StringSaver Saver;
 
+  /// Linker inputs originated from configuration file.
+  std::unique_ptr<llvm::opt::InputArgList> CfgLinkerInputs;
+
   /// Arguments originated from configuration file.
   std::unique_ptr<llvm::opt::InputArgList> CfgOptions;
 
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index ad14b5c9b6dc80..bfd440461ea601 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1062,6 +1062,15 @@ bool Driver::readConfigFile(StringRef FileName,
   for (Arg *A : *NewOptions)
     A->claim();
 
+  std::unique_ptr<InputArgList> NewLinkerIns = std::make_unique<InputArgList>();
+  for (Arg *A : NewOptions->filtered(options::OPT_l)) {
+    const Arg *BaseArg = &A->getBaseArg();
+    if (BaseArg == A)
+      BaseArg = nullptr;
+    appendOneArg(*NewLinkerIns, A, BaseArg);
+  }
+  NewOptions->eraseArg(options::OPT_l);
+
   if (!CfgOptions)
     CfgOptions = std::move(NewOptions);
   else {
@@ -1073,6 +1082,19 @@ bool Driver::readConfigFile(StringRef FileName,
       appendOneArg(*CfgOptions, Opt, BaseArg);
     }
   }
+
+  if (!CfgLinkerInputs)
+    CfgLinkerInputs = std::move(NewLinkerIns);
+  else {
+    // If this is a subsequent config file, append options to the previous one.
+    for (auto *Opt : *NewLinkerIns) {
+      const Arg *BaseArg = &Opt->getBaseArg();
+      if (BaseArg == Opt)
+        BaseArg = nullptr;
+      appendOneArg(*CfgLinkerInputs, Opt, BaseArg);
+    }
+  }
+
   ConfigFiles.push_back(std::string(CfgFileName));
   return false;
 }
@@ -1250,6 +1272,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   if (!ContainsError)
     ContainsError = loadConfigFiles();
   bool HasConfigFile = !ContainsError && (CfgOptions.get() != nullptr);
+  bool HasConfigLinkerIn = !ContainsError && (CfgLinkerInputs.get() != nullptr);
 
   // All arguments, from both config file and command line.
   InputArgList Args = std::move(HasConfigFile ? std::move(*CfgOptions)
@@ -1552,6 +1575,15 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
   // Construct the list of inputs.
   InputList Inputs;
   BuildInputs(C->getDefaultToolChain(), *TranslatedArgs, Inputs);
+  if (HasConfigLinkerIn && Inputs.size()) {
+    Arg *FinalPhaseArg;
+    if (getFinalPhase(*TranslatedArgs, &FinalPhaseArg) == phases::Link) {
+      DerivedArgList TranslatedLinkerIns(*CfgLinkerInputs);
+      for (Arg *A : *CfgLinkerInputs)
+        TranslatedLinkerIns.append(A);
+      BuildInputs(C->getDefaultToolChain(), TranslatedLinkerIns, Inputs);
+    }
+  }
 
   // Populate the tool chains for the offloading devices, if any.
   CreateOffloadingDeviceToolChains(*C, Inputs);
diff --git a/clang/test/Driver/Inputs/config-l-openmp.cfg b/clang/test/Driver/Inputs/config-l-openmp.cfg
new file mode 100644
index 00000000000000..91f8884b6e9e23
--- /dev/null
+++ b/clang/test/Driver/Inputs/config-l-openmp.cfg
@@ -0,0 +1 @@
+-Wall -fopenmp -lm -lhappy
diff --git a/clang/test/Driver/Inputs/config-l.cfg b/clang/test/Driver/Inputs/config-l.cfg
new file mode 100644
index 00000000000000..0ebc28baddbe2c
--- /dev/null
+++ b/clang/test/Driver/Inputs/config-l.cfg
@@ -0,0 +1 @@
+-Wall -lm -lhappy
diff --git a/clang/test/Driver/config-file.c b/clang/test/Driver/config-file.c
index 9df830ca4c7538..799e545e1e518b 100644
--- a/clang/test/Driver/config-file.c
+++ b/clang/test/Driver/config-file.c
@@ -82,3 +82,22 @@
 // CHECK-TWO-CONFIGS: -isysroot
 // CHECK-TWO-CONFIGS-SAME: /opt/data
 // CHECK-TWO-CONFIGS-SAME: -Wall
+
+//--- The -l flags should be moved to the end of input list and appear only when linking.
+// RUN: %clang --config %S/Inputs/config-l.cfg -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING
+// RUN: %clang --config %S/Inputs/config-l-openmp.cfg -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING-LIBOMP-GOES-LAST
+// RUN: %clang --config %S/Inputs/config-l.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
+// RUN: %clang --config %S/Inputs/config-l-openmp.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
+// CHECK-LINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
+// CHECK-LINKING: "-Wall"
+// CHECK-LINKING: "/tmp/{{.*}}-{{.*}}.o" "-lm" "-lhappy"
+// CHECK-LINKING-LIBOMP-GOES-LAST: Configuration file: {{.*}}Inputs{{.}}config-l-openmp.cfg
+// CHECK-LINKING-LIBOMP-GOES-LAST: "-Wall"
+// CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-fopenmp"
+// CHECK-LINKING-LIBOMP-GOES-LAST: "/tmp/{{.*}}-{{.*}}.o" "-lm" "-lhappy"
+// CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-lomp"
+// CHECK-NOLINKING: Configuration file: {{.*}}Inputs{{.}}config-l{{.*}}.cfg
+// CHECK-NOLINKING: "-Wall"
+// CHECK-NOLINKING-NO: "-lm"
+// CHECK-NOLINKING-NO: "-lhappy"
+// CHECK-NOLINKING-NO: "-lomp"
diff --git a/flang/test/Driver/Inputs/config-l-openmp.cfg b/flang/test/Driver/Inputs/config-l-openmp.cfg
new file mode 100644
index 00000000000000..46ab3273f84f19
--- /dev/null
+++ b/flang/test/Driver/Inputs/config-l-openmp.cfg
@@ -0,0 +1 @@
+-ffast-math -fopenmp -lm -lhappy
diff --git a/flang/test/Driver/Inputs/config-l.cfg b/flang/test/Driver/Inputs/config-l.cfg
new file mode 100644
index 00000000000000..af2d0a99475a2a
--- /dev/null
+++ b/flang/test/Driver/Inputs/config-l.cfg
@@ -0,0 +1 @@
+-ffast-math -lm -lhappy
diff --git a/flang/test/Driver/config-file.f90 b/flang/test/Driver/config-file.f90
index 70316dd971f36b..15b18f4420e6b3 100644
--- a/flang/test/Driver/config-file.f90
+++ b/flang/test/Driver/config-file.f90
@@ -61,3 +61,22 @@
 ! CHECK-TWO-CONFIGS-NEXT: Configuration file: {{.*}}Inputs{{.}}config2{{.}}config-4.cfg
 ! CHECK-TWO-CONFIGS: -ffp-contract=fast
 ! CHECK-TWO-CONFIGS: -O3
+
+!--- The -l flags should be moved to the end of input list and appear only when linking.
+! RUN: %flang --config %S/Inputs/config-l.cfg -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING
+! RUN: %flang --config %S/Inputs/config-l-openmp.cfg -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING-LIBOMP-GOES-LAST
+! RUN: %flang --config %S/Inputs/config-l.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
+! RUN: %flang --config %S/Inputs/config-l-openmp.cfg -S %s -### 2>&1 | FileCheck %s -check-prefix CHECK-NOLINKING
+! CHECK-LINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
+! CHECK-LINKING: "-ffast-math"
+! CHECK-LINKING: "/tmp/{{.*}}-{{.*}}.o" "-lm" "-lhappy"
+! CHECK-LINKING-LIBOMP-GOES-LAST: Configuration file: {{.*}}Inputs{{.}}config-l-openmp.cfg
+! CHECK-LINKING-LIBOMP-GOES-LAST: "-ffast-math"
+! CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-fopenmp"
+! CHECK-LINKING-LIBOMP-GOES-LAST: "/tmp/{{.*}}-{{.*}}.o" "-lm" "-lhappy"
+! CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-lomp"
+! CHECK-NOLINKING: Configuration file: {{.*}}Inputs{{.}}config-l{{.*}}.cfg
+! CHECK-NOLINKING: "-ffast-math"
+! CHECK-NOLINKING-NO: "-lm"
+! CHECK-NOLINKING-NO: "-lhappy"
+! CHECK-NOLINKING-NO: "-lomp"

@mgorny mgorny requested review from MaskRay and thesamesam November 25, 2024 16:48
Copy link
Member

@DavidTruby DavidTruby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to handle linker options differently so I'm in favour of this change in principle.

Am I right in thinking that if the config file puts things last, the -l options provided by users will come before the config file ones, and unlike other options that will lead to those libraries being chosen first? If so, I think that's the correct way to do things anyway, so I prefer that to the current approach of putting the ones from the config file first. It might be considered a breaking change though.

@mgorny's question has got me thinking and given me concerns; I think if the user passes -Wl,-Bstatic -lmystaticlib and the config file is adding -lmydynamiclib after that, things will fail, because the -Bstatic will also apply to the lib in the config file. So we need to do something to prevent situations like that.

I've also added a few comments so that we can hopefully get this working for Windows as well.

@pawosm-arm
Copy link
Contributor Author

I think it makes sense to handle linker options differently so I'm in favour of this change in principle.

Am I right in thinking that if the config file puts things last, the -l options provided by users will come before the config file ones, and unlike other options that will lead to those libraries being chosen first? If so, I think that's the correct way to do things anyway, so I prefer that to the current approach of putting the ones from the config file first. It might be considered a breaking change though.

@mgorny's question has got me thinking and given me concerns; I think if the user passes -Wl,-Bstatic -lmystaticlib and the config file is adding -lmydynamiclib after that, things will fail, because the -Bstatic will also apply to the lib in the config file. So we need to do something to prevent situations like that.

I've also added a few comments so that we can hopefully get this working for Windows as well.

That was actually easy to fix.

Copy link
Member

@DavidTruby DavidTruby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change LGTM in general but I'd like Windows to have tests before approving as we need this to be working there too

Copy link
Member

@DavidTruby DavidTruby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Would be good if someone from the clang side of things can approve as well, as this is a fairly big functionality change for config files.

Copy link
Member

@mgorny mgorny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything wrong but I don't consider myself an expert either.

@@ -82,3 +82,35 @@
// 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 -o %s.out %s -### 2>&1 | FileCheck %s -check-prefix CHECK-LINKING
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-o %s.out is strange. We typically use -o %t for output. That said, since we use -### and the output isn't useful, just omit it.

Copy link
Member

@MaskRay MaskRay Nov 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add something like -lxxx and -Wl,foo.a to test the position of config specified -l -Wl,`

Copy link
Contributor Author

@pawosm-arm pawosm-arm Nov 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the comments.
Added both things, and removed all -o's

// CHECK-LINKING-LIBOMP-GOES-LAST-SAME: "-lomp"
// CHECK-NOLINKING: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
// CHECK-NOLINKING: "-Wall"
// CHECK-NOLINKING-NO: "-lm"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the linker options are known be adjacent, place them on the same CHECK line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -1062,6 +1062,16 @@ bool Driver::readConfigFile(StringRef FileName,
for (Arg *A : *NewOptions)
A->claim();

std::unique_ptr<InputArgList> NewLinkerIns = std::make_unique<InputArgList>();
Copy link
Member

@MaskRay MaskRay Nov 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto since RHS makes the type clear.

This block of code needs a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed and added a comment

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

for (Arg *A : NewOptions->filtered(options::OPT_Wl_COMMA, options::OPT_l)) {
const Arg *BaseArg = &A->getBaseArg();
if (BaseArg == A)
BaseArg = nullptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getBaseArg() is always nullptr. I've cleaned up the existing code using it. Please rebase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rebased

! CHECK-LINKING: "-ffast-math"
! CHECK-LINKING: "{{.*}}-{{.*}}.o" "-lmylib" "foo.a" "-lm" "-Bstatic" "-lhappy" "-Bdynamic"
! CHECK-LINKING-LIBOMP-GOES-LAST: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
! CHECK-LINKING-LIBOMP-GOES-LAST: "-ffast-math"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If two options are adjacent, place them on the same SAME line. Otherwise, if they are on the same line in the output, use -SAME:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need some help here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, perhaps not here. But use -SAME: for some lines below, e.g. ! CHECK-NOLINKING-NO: "-lomp"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding "-fopenmp" may result in: "-latomic" "-lm" "-lomp"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems I had wrong idea of how "SAME:" works. I'll make adjustments you're suggesting here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed all SAME:

// CHECK-LINKING: "-Wall"
// CHECK-LINKING: "{{.*}}-{{.*}}.o" "-lmylib" "foo.a" "-lm" "-Bstatic" "-lhappy" "-Bdynamic"
// CHECK-LINKING-LIBOMP-GOES-LAST: Configuration file: {{.*}}Inputs{{.}}config-l.cfg
// CHECK-LINKING-LIBOMP-GOES-LAST: "-Wall"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If two options are adjacent, place them on the same SAME line. Otherwise, if they are on the same line in the output, use -SAME:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the changes have been made

else {
// If this is a subsequent config file, append options to the previous one.
for (auto *Opt : *NewLinkerIns) {
const Arg *BaseArg = &Opt->getBaseArg();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove getBaseArg

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

@pawosm-arm
Copy link
Contributor Author

This LGTM but I think the other reviewers need to take a look too. Can you change the title and description of the PR/commit to represent that what we're doing is different now?

The commit title and message has been changed already, the PR description comment too, I've updated the PR title.

@thesamesam
Copy link
Member

This looks good to me but no doc updates?

@pawosm-arm
Copy link
Contributor Author

This looks good to me but no doc updates?

Would a paragraph in clang/docs/UsersManual.rst "Configuration files" section do? If so, I'd write one.

@pawosm-arm
Copy link
Contributor Author

This looks good to me but no doc updates?

Added.

@pawosm-arm pawosm-arm requested a review from MaskRay December 4, 2024 23:10
@MaskRay
Copy link
Member

MaskRay commented Dec 5, 2024

It's worth spending more time discussing the metacharacter.

^ can be interpreted as ^ in regex, which means the beginning. $, on the other side, suggests the end.
While I agree that @ for response files is not a good choice, I personally would not rule out $ just because shell/makefile variable substitution uses it.

In CCC_OVERRIDE_OPTIONS (clang/lib/Driver/Driver.cpp), ^ is to add an option at the beginning, but there is no $. There is +, whose meaning is slightly different.

@@ -297,6 +297,9 @@ class Driver {
/// Object that stores strings read from configuration file.
llvm::StringSaver Saver;

/// Linker inputs originated from configuration file.
std::unique_ptr<llvm::opt::InputArgList> CfgLinkerInputs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CfgLinkerInputs is not a good name as the implementation is generic, not coupled with linker options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

@pawosm-arm
Copy link
Contributor Author

It's worth spending more time discussing the metacharacter.

^ can be interpreted as ^ in regex, which means the beginning. $, on the other side, suggests the end. While I agree that @ for response files is not a good choice, I personally would not rule out $ just because shell/makefile variable substitution uses it.

In CCC_OVERRIDE_OPTIONS (clang/lib/Driver/Driver.cpp), ^ is to add an option at the beginning, but there is no $. There is +, whose meaning is slightly different.

I have no spare time for discussing it, so I turned it to $


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the outer std::move is unneeded

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subject (linker flags) is no longer accurate.

It's better to mention that Add $ for ....

@pawosm-arm pawosm-arm changed the title [clang][driver] Special care for linker flags in config files [clang][driver] Use $ prefix with config file options to have them added after all of the command line options Dec 6, 2024
@pawosm-arm
Copy link
Contributor Author

The subject (linker flags) is no longer accurate.

It's better to mention that Add $ for ....

Reworded

Copy link

github-actions bot commented Dec 6, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

…ded after all of the command line options

Currently, if a -l (or -Wl,) flag is added into a config file
(e.g. clang.cfg), it is situated before any object file in the
effective command line. If the library requested by given -l flag is
static, its symbols will not be made visible to any of the object
files provided by the user. Also, the presence of any of the linker
flags in a config file confuses the driver whenever the user invokes
clang without any parameters (see issue llvm#67209).

This patch attempts to solve both of the problems, by allowing a split
of the arguments list into two parts. The head part of the list will
be used as before, but the tail part will be appended after the
command line flags provided by the user and only when it is known
that the linking should occur. The $-prefixed arguments will be added
to the tail part.
@pawosm-arm pawosm-arm merged commit 755519f into llvm:main Dec 7, 2024
9 checks passed
carlocab added a commit to carlocab/llvm-project that referenced this pull request Dec 9, 2024
This was added in llvm#117573 but the options were not being rendered
correctly due to the missing newline after `::`.
carlocab added a commit that referenced this pull request Dec 9, 2024
This was added in #117573 but the options were not being rendered
correctly due to the missing newline after `::`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category flang:driver flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants