Skip to content

[Multilib] Extend the Multilib system to support an IncludepPath field. #146651

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

simpal01
Copy link
Contributor

@simpal01 simpal01 commented Jul 2, 2025

This patch extends the Multilib Yaml format to allow specifying an IncludePath for the header path/s per multilib variant. The goal is to enable fine-grained control over header search paths for each multilib variant. This feature is especially useful in setups where header paths deviate from the default bare-metal assumptions. For example, when headers are shared across target triples, it becomes necessary to organize them under target-triple-specific directories to ensure correct resolution.

In the Clang driver, GCCSuffix, OSSuffix and IncludeSuffix are path suffixes that help the compiler locate the correct set of headers and libraries for the selected target. Clang’s multilib infrastructure uses the Multilib class to encapsulate these suffixes. Currently, in the bare-metal Clang driver, all of these suffixes — GCCSuffix, OSSuffix, and IncludeSuffix — are effectively mapped to the Dir field specified in the multilib.yaml configuration.

This patch allows it to be configured independently of Dir, enabling finer-grained control over header search paths for each multilib variant.

Key Changes:

  • New Field: IncludePath in multilib.yaml (header path or list of header paths, strings, optional).

  • When present, this path is passed to the Multilib constructor and appended to the sysroot during header path resolution. If omitted, behaviour defaults to preserving current behaviour.

For example,

Dir: aarch64-none-elf/aarch64a_exn_rtti_unaligned
Flags:
--target=aarch64-unknown-none-elf
IncludePath:
include
include-libunwind
aarch64-none-elf/include
aarch64-none-elf/aarch64a_exn_rtti_unaligned/include

Implementation Notes:

  1. Extend the YAML parser to read the IncludePath key.
  2. Update the Multilib constructor to store the content from this field.
  3. Ensure the driver logic reads and applies IncludePath correctly.

This patch extends the Multilib Yaml format to allow
specifying an IncludePath for the header path/s per
multilib variant. The goal is to enable fine-grained
control over header search paths for each multilib variant.
This feature is especially useful in setups where header
paths deviate from the default bare-metal assumptions.
For example, when headers are shared across target triples,
it becomes necessary to organize them under target-triple-specific
directories to ensure correct resolution.

In the Clang driver, GCCSuffix, OSSuffix and IncludeSuffix
are path suffixes that help the compiler locate the correct
set of headers and libraries for the selected target.
Clang’s multilib infrastructure uses the Multilib class to
encapsulate these suffixes. Currently, in the bare-metal
Clang driver, all of these suffixes — GCCSuffix, OSSuffix, and
IncludeSuffix — are effectively mapped to the Dir field specified
in the multilib.yaml configuration.

This patch allows it to be configured independently of Dir,
enabling finer-grained control over header search paths for each multilib variant.

Key Changes:
New Field: IncludePath in multilib.yaml (header path or list of header
paths, strings, optional). When present, this path is passed to the
Multilib constructor and appended to the sysroot during header path resolution.
If omitted, behaviour defaults to preserving current behaviour.

For example,
- Dir: aarch64-none-elf/aarch64a_exn_rtti_unaligned
  Flags:
  - --target=aarch64-unknown-none-elf
  IncludePath:
  - include
  - include-libunwind
  - aarch64-none-elf/include
  - aarch64-none-elf/aarch64a_exn_rtti_unaligned/include

Implementation Notes:
1. Extend the YAML parser to read the IncludePath key.
2. Update the Multilib constructor to store the content from this field.
3. Ensure the driver logic reads and applies IncludePath correctly.
@simpal01 simpal01 marked this pull request as draft July 2, 2025 08:36
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Jul 2, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 2, 2025

@llvm/pr-subscribers-clang-driver

@llvm/pr-subscribers-clang

Author: Simi Pallipurath (simpal01)

Changes

This patch extends the Multilib Yaml format to allow specifying an IncludePath for the header path/s per multilib variant. The goal is to enable fine-grained control over header search paths for each multilib variant. This feature is especially useful in setups where header paths deviate from the default bare-metal assumptions. For example, when headers are shared across target triples, it becomes necessary to organize them under target-triple-specific directories to ensure correct resolution.

In the Clang driver, GCCSuffix, OSSuffix and IncludeSuffix are path suffixes that help the compiler locate the correct set of headers and libraries for the selected target. Clang’s multilib infrastructure uses the Multilib class to encapsulate these suffixes. Currently, in the bare-metal Clang driver, all of these suffixes — GCCSuffix, OSSuffix, and IncludeSuffix — are effectively mapped to the Dir field specified in the multilib.yaml configuration.

This patch allows it to be configured independently of Dir, enabling finer-grained control over header search paths for each multilib variant.

Key Changes:

  • New Field: IncludePath in multilib.yaml (header path or list of header paths, strings, optional).

  • When present, this path is passed to the Multilib constructor and appended to the sysroot during header path resolution. If omitted, behaviour defaults to preserving current behaviour.

For example,

  • Dir: aarch64-none-elf/aarch64a_exn_rtti_unaligned Flags:
    • --target=aarch64-unknown-none-elf IncludePath:
    • include
    • include-libunwind
    • aarch64-none-elf/include
    • aarch64-none-elf/aarch64a_exn_rtti_unaligned/include

Implementation Notes:

  1. Extend the YAML parser to read the IncludePath key.
  2. Update the Multilib constructor to store the content from this field.
  3. Ensure the driver logic reads and applies IncludePath correctly.

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

3 Files Affected:

  • (modified) clang/include/clang/Driver/Multilib.h (+7)
  • (modified) clang/lib/Driver/Multilib.cpp (+12-3)
  • (modified) clang/lib/Driver/ToolChains/BareMetal.cpp (+29-8)
diff --git a/clang/include/clang/Driver/Multilib.h b/clang/include/clang/Driver/Multilib.h
index fc071ef48ca0f..1f1372508c480 100644
--- a/clang/include/clang/Driver/Multilib.h
+++ b/clang/include/clang/Driver/Multilib.h
@@ -35,12 +35,14 @@ class Driver;
 class Multilib {
 public:
   using flags_list = std::vector<std::string>;
+  using includepath_list = std::vector<std::string>;
 
 private:
   std::string GCCSuffix;
   std::string OSSuffix;
   std::string IncludeSuffix;
   flags_list Flags;
+  includepath_list IncludePath;
 
   // Optionally, a multilib can be assigned a string tag indicating that it's
   // part of a group of mutually exclusive possibilities. If two or more
@@ -62,6 +64,7 @@ class Multilib {
   /// This is enforced with an assert in the constructor.
   Multilib(StringRef GCCSuffix = {}, StringRef OSSuffix = {},
            StringRef IncludeSuffix = {}, const flags_list &Flags = flags_list(),
+           const includepath_list &IncludePath = includepath_list(),
            StringRef ExclusiveGroup = {},
            std::optional<StringRef> Error = std::nullopt);
 
@@ -81,6 +84,10 @@ class Multilib {
   /// All elements begin with either '-' or '!'
   const flags_list &flags() const { return Flags; }
 
+  /// Get the include paths specified in multilib.yaml under the 'IncludePath'
+  /// field
+  const includepath_list &includePath() const { return IncludePath; }
+
   /// Get the exclusive group label.
   const std::string &exclusiveGroup() const { return ExclusiveGroup; }
 
diff --git a/clang/lib/Driver/Multilib.cpp b/clang/lib/Driver/Multilib.cpp
index 87fa1af54a8ea..2cbb47dd7acdb 100644
--- a/clang/lib/Driver/Multilib.cpp
+++ b/clang/lib/Driver/Multilib.cpp
@@ -29,9 +29,11 @@ using namespace llvm::sys;
 
 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
                    StringRef IncludeSuffix, const flags_list &Flags,
+                   const includepath_list &IncludePath,
                    StringRef ExclusiveGroup, std::optional<StringRef> Error)
     : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
-      Flags(Flags), ExclusiveGroup(ExclusiveGroup), Error(Error) {
+      Flags(Flags), IncludePath(IncludePath), ExclusiveGroup(ExclusiveGroup),
+      Error(Error) {
   assert(GCCSuffix.empty() ||
          (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1));
   assert(OSSuffix.empty() ||
@@ -299,6 +301,7 @@ struct MultilibSerialization {
   std::string Dir;        // if this record successfully selects a library dir
   std::string Error;      // if this record reports a fatal error message
   std::vector<std::string> Flags;
+  std::vector<std::string> IncludePath;
   std::string Group;
 };
 
@@ -350,6 +353,7 @@ template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
     io.mapOptional("Dir", V.Dir);
     io.mapOptional("Error", V.Error);
     io.mapRequired("Flags", V.Flags);
+    io.mapOptional("IncludePath", V.IncludePath);
     io.mapOptional("Group", V.Group);
   }
   static std::string validate(IO &io, MultilibSerialization &V) {
@@ -359,6 +363,10 @@ template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
       return "the 'Dir' and 'Error' keys may not both be specified";
     if (StringRef(V.Dir).starts_with("/"))
       return "paths must be relative but \"" + V.Dir + "\" starts with \"/\"";
+    for (const auto &Path : V.IncludePath) {
+      if (StringRef(Path).starts_with("/"))
+        return "paths must be relative but \"" + Path + "\" starts with \"/\"";
+    }
     return std::string{};
   }
 };
@@ -489,7 +497,8 @@ MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
   Multilibs.reserve(MS.Multilibs.size());
   for (const auto &M : MS.Multilibs) {
     if (!M.Error.empty()) {
-      Multilibs.emplace_back("", "", "", M.Flags, M.Group, M.Error);
+      Multilibs.emplace_back("", "", "", M.Flags, M.IncludePath, M.Group,
+                             M.Error);
     } else {
       std::string Dir;
       if (M.Dir != ".")
@@ -498,7 +507,7 @@ MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
       // Multilib constructor. If we later support more than one type of group,
       // we'll have to look up the group name in MS.Groups, check its type, and
       // decide what to do here.
-      Multilibs.emplace_back(Dir, Dir, Dir, M.Flags, M.Group);
+      Multilibs.emplace_back(Dir, Dir, Dir, M.Flags, M.IncludePath, M.Group);
     }
   }
 
diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp
index 981395deb9dbc..dfa801ce887c7 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.cpp
+++ b/clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -417,10 +417,20 @@ void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
   const SmallString<128> SysRootDir(computeSysRoot());
   if (!SysRootDir.empty()) {
     for (const Multilib &M : getOrderedMultilibs()) {
-      SmallString<128> Dir(SysRootDir);
-      llvm::sys::path::append(Dir, M.includeSuffix());
-      llvm::sys::path::append(Dir, "include");
-      addSystemInclude(DriverArgs, CC1Args, Dir.str());
+      // Add include paths specified in multilib.yaml under the 'IncludePath'
+      // field
+      if (!M.includePath().empty()) {
+        for (const std::string &Path : M.includePath()) {
+          SmallString<128> Dir(SysRoot);
+          llvm::sys::path::append(Dir, Path);
+          addSystemInclude(DriverArgs, CC1Args, Dir.str());
+        }
+      } else {
+        SmallString<128> Dir(SysRootDir);
+        llvm::sys::path::append(Dir, M.includeSuffix());
+        llvm::sys::path::append(Dir, "include");
+        addSystemInclude(DriverArgs, CC1Args, Dir.str());
+      }
     }
   }
 }
@@ -501,10 +511,21 @@ void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
         addSystemInclude(DriverArgs, CC1Args, TargetDir.str());
         break;
       }
-      // Add generic path if nothing else succeeded so far.
-      llvm::sys::path::append(Dir, "include", "c++", "v1");
-      addSystemInclude(DriverArgs, CC1Args, Dir.str());
-      break;
+      if (!M.includePath().empty()) {
+        // Add include paths specified in multilib.yaml under the 'IncludePath'
+        // field
+        for (const std::string &Path : M.includePath()) {
+          Dir = SysRoot;
+          llvm::sys::path::append(Dir, Path, "c++", "v1");
+          addSystemInclude(DriverArgs, CC1Args, Dir.str());
+        }
+        break;
+      } else {
+        // Add generic path if nothing else succeeded so far.
+        llvm::sys::path::append(Dir, "include", "c++", "v1");
+        addSystemInclude(DriverArgs, CC1Args, Dir.str());
+        break;
+      }
     }
     case ToolChain::CST_Libstdcxx: {
       llvm::sys::path::append(Dir, "include", "c++");

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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants