From 85a7f9e078ecc7fe68bb5b177d244cb511e26dbb Mon Sep 17 00:00:00 2001 From: mercury233 Date: Tue, 10 Mar 2026 11:20:51 +0800 Subject: [PATCH] add os.findsubdirheader --- src/base/os.lua | 41 +++++++++++++++++++ tests/base/test_os.lua | 29 +++++++++++++ .../subfolder/include/testlib/testlib2.h | 1 + website/docs/os/os.findheader.md | 10 ++++- website/docs/os/os.findsubdirheader.md | 36 ++++++++++++++++ website/sidebars.js | 1 + 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/folder/subfolder/include/testlib/testlib2.h create mode 100644 website/docs/os/os.findsubdirheader.md diff --git a/src/base/os.lua b/src/base/os.lua index b40c818f1f..3ce0f39547 100644 --- a/src/base/os.lua +++ b/src/base/os.lua @@ -165,6 +165,47 @@ return result end + function os.findsubdirheader(headerpath, additionalpaths) + -- headerpath: a partial header file path + -- additionalpaths: required; a string or table of subdirectory paths + + if additionalpaths == nil then + error("os.findsubdirheader: additionalpaths is required", 2) + end + + local paths = get_library_search_path() + + -- replace all /lib and /bin by /include + paths = table.translate(paths, function (p) return p:gsub('[/\\]lib[0-9]*', '/include'):gsub('[/\\]bin', '/include') end) + + local userpaths = {} + + if type(additionalpaths) == "string" then + userpaths = { additionalpaths } + elseif type(additionalpaths) == "table" then + userpaths = additionalpaths + end + + if #userpaths > 0 then + local basepaths = paths + local newpaths = {} + for _, userpath in ipairs(userpaths) do + if path.isabsolute(userpath) then + -- absolute path: search directly, same as os.findheader + table.insert(newpaths, userpath) + else + for _, p in ipairs(basepaths) do + table.insert(newpaths, path.join(p, userpath)) + end + end + end + paths = newpaths + end + + local result = os.pathsearch(headerpath, table.unpack(paths)) + return result + end + -- -- Retrieve the current target operating system ID string. -- diff --git a/tests/base/test_os.lua b/tests/base/test_os.lua index 421cf5554a..800e0a1c80 100644 --- a/tests/base/test_os.lua +++ b/tests/base/test_os.lua @@ -102,6 +102,35 @@ os.findheader("test.h", path.getabsolute("folder/subfolder/include"))) end + function suite.findsubdirheader_provided_relative() + local os_getenv = os.getenv + os.getenv = create_mock_os_getenv({ [get_LD_PATH_variable_name()] = get_surrounded_env_path("folder/subfolder/lib") }) + + test.isequal(path.getabsolute("folder/subfolder/include/testlib"), os.findsubdirheader("testlib2.h", "testlib")) + + os.getenv = os_getenv + end + + function suite.findsubdirheader_failure() + test.isfalse(os.findsubdirheader("Knights/who/say/Ni.hpp", "nonexistent")) + end + + function suite.findsubdirheader_provided_absolute() + test.isequal(path.getabsolute("folder/subfolder/include"), + os.findsubdirheader("test.h", path.getabsolute("folder/subfolder/include"))) + end + + function suite.findsubdirheader_mixed_with_empty() + local os_getenv = os.getenv + os.getenv = create_mock_os_getenv({ [get_LD_PATH_variable_name()] = get_surrounded_env_path("folder/subfolder/lib") }) + + -- "testlib" finds testlib2.h; "" allows test.h to be found in the base include dir + test.isequal(path.getabsolute("folder/subfolder/include/testlib"), os.findsubdirheader("testlib2.h", {"testlib", ""})) + test.isequal(path.getabsolute("folder/subfolder/include"), os.findsubdirheader("test.h", {"testlib", ""})) + + os.getenv = os_getenv + end + function suite.findheader_frompath_lib() local os_getenv = os.getenv os.getenv = create_mock_os_getenv({ [get_LD_PATH_variable_name()] = get_surrounded_env_path("folder/subfolder/lib") }) diff --git a/tests/folder/subfolder/include/testlib/testlib2.h b/tests/folder/subfolder/include/testlib/testlib2.h new file mode 100644 index 0000000000..a37207cc09 --- /dev/null +++ b/tests/folder/subfolder/include/testlib/testlib2.h @@ -0,0 +1 @@ +// Only used for presence in tests diff --git a/website/docs/os/os.findheader.md b/website/docs/os/os.findheader.md index dada8efa28..405b91d87d 100644 --- a/website/docs/os/os.findheader.md +++ b/website/docs/os/os.findheader.md @@ -8,12 +8,20 @@ p = os.findheader("headerfile" [, additionalpaths]) `headerfile` is a file name or a the end of a file path to locate. -`additionalpaths` is a string or a table of one or more additional search path. +`additionalpaths` is a string or a table of one or more additional search path. Absolute paths are searched directly; relative paths are resolved against the current working directory. ### Return Value ### The path containing the header file, if found. Otherwise, nil. +### Example ### + +``` lua +os.findheader("stdlib.h") -- e.g. /usr/include +os.findheader("freetype2/ft2build.h") -- e.g. /usr/include +os.findheader("ft2build.h", {"/usr/local/include/freetype2", "/usr/include/freetype2"}) -- e.g. /usr/include/freetype2 +``` + ### Remarks ### `os.findheader` mostly use the same paths as [[os.findlib]] but replace `/lib` by `/include`. diff --git a/website/docs/os/os.findsubdirheader.md b/website/docs/os/os.findsubdirheader.md new file mode 100644 index 0000000000..27c38994a6 --- /dev/null +++ b/website/docs/os/os.findsubdirheader.md @@ -0,0 +1,36 @@ +Scan the well-known system locations looking for a header file, with support for subdirectory paths. + +```lua +p = os.findsubdirheader("headerfile", additionalpaths) +``` + +### Parameters ### + +`headerfile` is a file name or the end of a file path to locate. + +`additionalpaths` is a required string or table of one or more subdirectory paths: + +- **Relative paths** are joined with every default include search path (cross-join); only the resulting subdirectories are searched. +- **Absolute paths** are searched directly, acting the same behaviour of [[os.findheader]]. +- **Empty string `""`** is treated as a path segment that resolves to the base include path itself, so it can be mixed with other entries (e.g. `{"freetype2", ""}` searches both `/freetype2` and ``). + +### Return Value ### + +The path containing the header file, if found. Otherwise, nil. + +### Example ### + +``` lua +os.findsubdirheader("ft2build.h", "freetype2") -- e.g. /usr/include/freetype2 +os.findsubdirheader("gl.h", {"OpenGL", "GL"}) -- e.g. /usr/include/GL +os.findsubdirheader("ft2build.h", "/your/path/to/freetype2") -- e.g. /your/path/to/freetype2 +``` + +### Remarks ### +Unlike [[os.findheader]], relative paths in `additionalpaths` are resolved against each default search path, allowing discovery of headers in named subdirectories without requiring an absolute path. When `additionalpaths` is non-empty, **only** the specified candidate directories are searched. + +`os.findsubdirheader` uses the same base paths as [[os.findheader]], which mostly uses the same paths as [[os.findlib]] but replace `/lib` by `/include`. + +### Availability ### + +Premake 5.0-beta9 or later. diff --git a/website/sidebars.js b/website/sidebars.js index d0f10a35e3..23d37a2021 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -383,6 +383,7 @@ module.exports = { 'os/os.execute', 'os/os.executef', 'os/os.findheader', + 'os/os.findsubdirheader', 'os/os.findlib', 'os/os.get', 'os/os.getcwd',