Skip to content

Add toolsetpath API for specifying tool executable paths #2462

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions src/_premake_init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,14 @@
end,
}

api.register {
name = "toolsetpaths",
scope = "config",
kind = "keyed:keyed:path", -- { toolset_name = { tool_name = path } }
tokens = true,
pathVars = true,
}

api.register {
name = "undefines",
scope = "config",
Expand Down
37 changes: 37 additions & 0 deletions src/base/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1205,3 +1205,40 @@
function newoption(opt)
p.option.add(opt)
end


---
-- Set the path for a specific tool within a toolset.
--
-- @param toolsetName
-- The name of the toolset (e.g., "gcc", "clang").
-- @param toolName
-- The name of the tool (e.g., "cc", "cxx", "ld").
-- @param toolPath
-- The path to the tool executable.
---
function toolsetpath(toolsetName, toolName, toolPath)
local cfg = api.scope.current
if not cfg then
error("toolsetpath must be called within a configuration block", 2)
end

-- Get the toolsetpaths field definition
local field = p.field.get("toolsetpaths")
if not field then
error("toolsetpaths field not registered", 2)
end

-- Construct the data table
local data = {
[toolsetName] = {
[toolName] = toolPath
}
}

-- Store the data using configset.store
local status, err = configset.store(cfg, field, data)
if err then
error(err, 2)
end
end
5 changes: 5 additions & 0 deletions src/tools/clang.lua
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@
}

function clang.gettoolname(cfg, tool)
-- Check toolsetpaths first
if cfg.toolsetpaths and cfg.toolsetpaths[cfg.toolset] and cfg.toolsetpaths[cfg.toolset][tool] then
return cfg.toolsetpaths[cfg.toolset][tool]
end

local toolset, version = p.tools.canonical(cfg.toolset or p.CLANG)
local value = clang.tools[tool]
if type(value) == "function" then
Expand Down
5 changes: 5 additions & 0 deletions src/tools/cosmocc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@ cosmocc.tools = {
}

function cosmocc.gettoolname(cfg, tool)
-- Check toolsetpaths first
if cfg.toolsetpaths and cfg.toolsetpaths[cfg.toolset] and cfg.toolsetpaths[cfg.toolset][tool] then
return cfg.toolsetpaths[cfg.toolset][tool]
end

return cosmocc.tools[tool]
end
5 changes: 5 additions & 0 deletions src/tools/dotnet.lua
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@
--

function dotnet.gettoolname(cfg, tool)
-- Check toolsetpaths first
if cfg.toolsetpaths and cfg.toolsetpaths[cfg.toolset] and cfg.toolsetpaths[cfg.toolset][tool] then
return cfg.toolsetpaths[cfg.toolset][tool]
end

local compilers = {
msnet = "csc",
mono = "mcs",
Expand Down
6 changes: 6 additions & 0 deletions src/tools/emcc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ emcc.tools = {
-- flags correctly to emcc builds.
emcc.shared.profile = nil
emcc.ldflags.profile = nil
emcc.getsharedlibarg = function(cfg) return "" end

function emcc.gettoolname(cfg, tool)
-- Check toolsetpaths first
if cfg.toolsetpaths and cfg.toolsetpaths[cfg.toolset] and cfg.toolsetpaths[cfg.toolset][tool] then
return cfg.toolsetpaths[cfg.toolset][tool]
end

return emcc.tools[tool]
end
5 changes: 5 additions & 0 deletions src/tools/gcc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,11 @@
}

function gcc.gettoolname(cfg, tool)
-- Check toolsetpaths first
if cfg.toolsetpaths and cfg.toolsetpaths[cfg.toolset] and cfg.toolsetpaths[cfg.toolset][tool] then
return cfg.toolsetpaths[cfg.toolset][tool]
end

local toolset, version = p.tools.canonical(cfg.toolset or p.GCC)
if toolset == p.tools.gcc and version ~= nil then
version = "-" .. version
Expand Down
5 changes: 5 additions & 0 deletions src/tools/msc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,11 @@
--

function msc.gettoolname(cfg, tool)
-- Check toolsetpaths first
if cfg.toolsetpaths and cfg.toolsetpaths[cfg.toolset] and cfg.toolsetpaths[cfg.toolset][tool] then
return cfg.toolsetpaths[cfg.toolset][tool]
end

return nil
end

Expand Down
5 changes: 5 additions & 0 deletions src/tools/snc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@
}

function snc.gettoolname(cfg, tool)
-- Check toolsetpaths first
if cfg.toolsetpaths and cfg.toolsetpaths[cfg.toolset] and cfg.toolsetpaths[cfg.toolset][tool] then
return cfg.toolsetpaths[cfg.toolset][tool]
end

local names = snc.tools[cfg.architecture] or snc.tools[cfg.system] or {}
return names[tool]
end
10 changes: 10 additions & 0 deletions tests/tools/test_clang.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@
test.isequal("windres-16", clang.gettoolname(cfg, "rc"))
end

--
-- Verify that toolsetpath overrides the default tool name.
--
function suite.toolsetpathOverridesDefault()
toolset "clang"
toolsetpath("clang", "cc", "/path/to/my/custom/clang")
prepare()
test.isequal("/path/to/my/custom/clang", clang.gettoolname(cfg, "cc"))
end

--
-- Check Mac OS X deployment target flags
--
Expand Down
10 changes: 10 additions & 0 deletions tests/tools/test_dotnet.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
test.isequal("csc", dotnet.gettoolname(cfg, "csc"))
end

--
-- Verify that toolsetpath overrides the default tool name.
--
function suite.toolsetpathOverridesDefault()
toolset "dotnet"
toolsetpath("dotnet", "csc", "/path/to/my/custom/csc")
prepare()
test.isequal("/path/to/my/custom/csc", dotnet.gettoolname(cfg, "csc"))
end


--
-- Check support for the `csversion` API
Expand Down
10 changes: 10 additions & 0 deletions tests/tools/test_emcc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,13 @@ function suite.tools_onWASM64()
test.isequal("wasm64", cfg.architecture)
end

--
-- Verify that toolsetpath overrides the default tool name.
--
function suite.toolsetpathOverridesDefault()
toolset "emcc"
toolsetpath("emcc", "cc", "/path/to/my/custom/emcc")
prepare()
test.isequal("/path/to/my/custom/emcc", emcc.gettoolname(cfg, "cc"))
end

10 changes: 10 additions & 0 deletions tests/tools/test_gcc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@
test.isequal("windres-16", gcc.gettoolname(cfg, "rc"))
end

--
-- Verify that toolsetpath overrides the default tool name.
--
function suite.toolsetpathOverridesDefault()
toolset "gcc"
toolsetpath("gcc", "cc", "/path/to/my/custom/gcc")
prepare()
test.isequal("/path/to/my/custom/gcc", gcc.gettoolname(cfg, "cc"))
end

--
-- By default, the -MMD -MP are used to generate dependencies.
--
Expand Down
10 changes: 10 additions & 0 deletions tests/tools/test_msc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@
end


--
-- Verify that toolsetpath overrides the default tool name.
--
function suite.toolsetpathOverridesDefault()
toolset "msc"
toolsetpath("msc", "cc", "C:/path/to/my/custom/cl.exe")
prepare()
test.isequal("C:/path/to/my/custom/cl.exe", msc.gettoolname(cfg, "cc"))
end

--
-- Check the optimization flags.
--
Expand Down
10 changes: 10 additions & 0 deletions tests/tools/test_snc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
test.isnil(snc.gettoolname(cfg, "ar"))
end

--
-- Verify that toolsetpath overrides the default tool name.
--
function suite.toolsetpathOverridesDefault()
toolset "snc"
toolsetpath("snc", "cc", "/path/to/my/custom/snc_cc")
prepare()
test.isequal("/path/to/my/custom/snc_cc", snc.gettoolname(cfg, "cc"))
end


--
-- By default, the -MMD -MP are used to generate dependencies.
Expand Down
41 changes: 41 additions & 0 deletions website/docs/toolsetpath.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Specifies the path to a specific tool executable for a given toolset. This allows overriding the default tool lookup behavior of Premake.

```lua
toolsetpath(toolsetName, toolName, toolPath)
```

### Parameters

`toolsetName`
: The name of the toolset (e.g., `"gcc"`, `"clang"`, `"msc"`).

`toolName`
: The name of the tool within the toolset (e.g., `"cc"` for C compiler, `"cxx"` for C++ compiler, `"ld"` for linker, `"ar"` for archiver).

`toolPath`
: The absolute or relative path to the tool executable.

### Applies To

Project configurations.

### Example

```lua
project "MyProject"
kind "ConsoleApp"
language "C++"
system "Linux"

configuration "Release"
toolset "gcc"
-- Specify a custom path for the GCC C++ compiler
toolsetpath("gcc", "cxx", "/opt/my_custom_gcc/bin/g++")

configuration "Debug"
toolset "clang"
-- Specify a custom path for the Clang C compiler
toolsetpath("clang", "cc", "/usr/local/clang-15/bin/clang")
```

In this example, the `toolsetpath` function is used to specify custom paths for the C++ compiler in the "Release" configuration (using GCC) and the C compiler in the "Debug" configuration (using Clang).
53 changes: 53 additions & 0 deletions website/docs/toolsetpaths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Specifies custom paths for tool executables for one or more toolsets using a nested table structure. This field is primarily intended for internal use by Premake modules and actions. **End users should generally prefer using [`toolsetpath`](toolsetpath.md)** for specifying custom tool paths, as it provides a more user-friendly syntax.

```lua
toolsetpaths {
["toolsetName"] = {
["toolName"] = "toolPath",
-- ... more tools for this toolset
},
-- ... more toolsets
}
```

### Parameters

The `toolsetpaths` field accepts a table where keys are `toolsetName` (the name of the toolset, e.g., `"gcc"`, `"clang"`, `"msc"`) and values are nested tables.

The nested tables have `toolName` (the name of the tool within the toolset, e.g., `"cc"` for C compiler, `"cxx"` for C++ compiler, `"ld"` for linker, `"ar"`) as keys and `toolPath` (the absolute or relative path to the tool executable) as values.

### Applies To

Project configurations.

### Example

```lua
project "MyProject"
kind "ConsoleApp"
language "C++"
system "Linux"

configuration "Release"
toolset "gcc"
-- Example of using the toolsetpaths field (less common for end users)
toolsetpaths {
gcc = {
cc = "/opt/my_custom_gcc/bin/gcc",
cxx = "/opt/my_custom_gcc/bin/g++",
ar = "/opt/my_custom_gcc/bin/ar"
}
}

configuration "Debug"
toolset "clang"
-- Prefer using the toolsetpath function for clarity
toolsetpath("clang", "cc", "/usr/local/clang-15/bin/clang")
toolsetpath("clang", "ld", "/usr/local/clang-15/bin/ld")
```

In this example, both the `toolsetpaths` field and the `toolsetpath` function are shown. The `toolsetpath` function is the recommended approach for end users.

### See Also ###

* [toolsetpath](toolsetpath.md)
1 change: 1 addition & 0 deletions website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ module.exports = {
'thumbmode',
'toolchainversion',
'toolset',
'toolsetpath',
'toolsversion',
'undefines',
'usage',
Expand Down
Loading