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',