General problem:
I get absolute paths in my INTERFACE_LINK_LIBRARIES and INTERFACE_INCLUDE_DIRECTORIES properties for a target, which is a problem because these variables get exported via the install(EXPORT), which will mean trouble if anything is relocated. I traced the origin of these absolute paths back to the find_package command, which (for some but not all packages) sets variables like Boost_LIBRARIES with absolute paths. When they are used with e.g. the target_link_libraries command, they end up in the INTERFACE_LINK_LIBRARIES.
Q: How do I best avoid the exported target properties from being poluted with absolute paths?
The trivial solution is obviously to just overwrite the respective variables with some strings that only name the libraries (hardcoded), but I guess there should be a better way.
Specific problem (Example): see https://github.com/cartographer-project/cartographer/issues/1688
EDIT:
My (modified) cartographer-config.cmake.in
(note: you can ignore the stuff associated to ceres, since that does not give me trouble, eventhough it places an abs path in the hint):
# Copyright 2016 The Cartographer Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Usage from an external project:
# In your CMakeLists.txt, add these lines:
#
# find_package(cartographer REQUIRED)
# target_link_libraries(MY_TARGET_NAME PUBLIC cartographer)
#PACKAGE_INIT#
set_and_check(CARTOGRAPHER_CMAKE_DIR "#PACKAGE_CARTOGRAPHER_CMAKE_DIR#")
find_dependency(Boost)
find_dependency(Lua)
set(CERES_DIR_HINTS #Ceres_DIR#)
if (cartographer_FIND_QUIETLY)
find_package(Ceres QUIET HINTS ${CERES_DIR_HINTS})
elseif (cartographer_FIND_REQUIRED)
find_package(Ceres REQUIRED HINTS ${CERES_DIR_HINTS})
else ()
find_package(Ceres HINTS ${CERES_DIR_HINTS})
endif()
include("${CARTOGRAPHER_CMAKE_DIR}/CartographerTargets.cmake")
How do I best avoid the exported target properties from being poluted with absolute paths?
Hide absolute paths behind the IMPORTED target:
# Express an external library via IMPORTED target
add_library(lib_external IMPORTED)
set_target_properties(lib_external PROPERTIES
IMPORTED_LOCATION "${XXX_LIBRARY} (some absolute path)"
INTERFACE_INCLUDE_DIRECTORIES "${XXX_INCLUDE_DIRECTORIES} (some absolute path)"
)
# Use external library via the target created
target_link_libraries(my_lib lib_external)
Such way, the config file for my_lib will contain only the name of the IMPORTED library, but not its properties.
In the config file for your project you just need to create IMPORTED target with the same name and fill its properties according to the current machine (where your project is used).
find_package for most of modern packages (including Boost) already returns an IMPORTED target. In the configuration file for your project you may use find_dependency, which discovers the package on the current machine and creates an IMPORTED target again.
For old packages, which find_package just returns XXX_LIBRARIES and XXX_INCLUDE_DIRECTORIES variables, you may create the IMPORTED target manually. In the configuration file for your project you may use find_dependency too, and again create IMPORTED target manually.
When use find_dependency macro, remember to pass the same parameters which you have passed to the find_package() in the project's CMakeLists.txt. This is primary about COMPONENTS parameter and variables like Boost_USE_STATIC_LIBS, which affects on the package searching.
If you want to specify VERSION requirement, then this requirement should express binary compatibility of the library on the target machine with the library against which the project has been actually built.
HINST and PATHS options for find_package are unlikely needed to be transferred to the find_dependency: call to the find_dependency is processed on the target machine, which paths could be different from ones on the build machine.
Related
I am writing a C++ library (header-only) and am using CMake to generate my (Visual Studio) project and solution files. I'm also writing a test suite, which is part of the same CMake project.
My problem occurs when I call target_include_directories() on the target that represents my header-only library, so that consumers of my library may find its header files. I get the following error message (even though generation is NOT aborted).
CMake Error in CMakeLists.txt:
Target "Fonts" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"D:/Projects/GPC/fonts/include"
which is prefixed in the source directory.
(D:/Projects/GPC/Fonts being the top-level directory of my library project. Btw the problem remains if I move my header files to the top directory.)
The offending line in my CMakeLists.txt is this (adapted for simplicity):
target_include_directories(Fonts INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
I do not understand what I'm doing wrong. Without target_include_directories(), code of consumer projects simply can't include my header files (unless in installed form, but I haven't gotten to that yet, and in any case I want to be able to use my library from its build tree, without installation.)
I feel like I'm missing something basic here; yet I've searched for hours without finding a solution or explanation.
The origin of the problem is not the target_include_directories command itself, but the attempt to install a target that has a public or interface include directory prefixed in the source path (i.e. the include directory is a subdirectory of your ${PROJECT_SOURCE_DIR}.)
While it is perfectly fine and desirable to use absolute paths when building the library from scratch, a third party library that pulls in a prebuilt version of that library will probably want to use a different include path. After all, you do not want all of your users to mirror the directory structure of your build machine, just to end up in the right include path.
CMake's packaging mechanism provides support for both of these use cases: You may pull in a library directly from the build tree (that is, check out the source, build it, and point find_package() to the directory), or from an install directory (run make INSTALL to copy built stuff to the install directory and point find_package() to that directory). The latter approach needs to be relocatable (that is, I build and install on my machine, send you the resulting directory and you will be able to use it on your machine from a different directory structure), while the former is not.
This is a very neat feature, but you have to account for it when setting up the include directories. Quoting the manual for target_include_directories:
Include directories usage requirements commonly differ between the
build-tree and the install-tree. The BUILD_INTERFACE and
INSTALL_INTERFACE generator expressions can be used to describe
separate usage requirements based on the usage location. Relative
paths are allowed within the INSTALL_INTERFACE expression and are
interpreted relative to the installation prefix. For example:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
$<INSTALL_INTERFACE:include/mylib> # <prefix>/include/mylib
)
The BUILD_INTERFACE and INSTALL_INTERFACE generator expressions do all the magic:
$<INSTALL_INTERFACE:...>
Content of ... when the property is exported using install(EXPORT), and empty otherwise.
$<BUILD_INTERFACE:...>
Content of ... when the property is exported using export(), or when the target is used by another target in the same buildsystem.
Expands to the empty string otherwise.
I'm running cmake v 3.18.0 on ubuntu 20.04
My install() command doesn't generate any install rules within the generated Makfile. CMakeLists.txt looks like this:
# Static library
add_library(mbaux
STATIC
mb_cheb.c mb_delaun.c mb_intersectgrid.c mb_readwritegrd.c
mb_surface.c mb_track.c mb_truecont.c mb_zgrid.c)
target_include_directories(mbaux
PUBLIC
.
${X11_INCLUDE_DIR}
${GMT_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/src/mbio)
# Static library
add_library(mbxgr
STATIC
mb_xgraphics.c)
target_include_directories(mbxgr
PUBLIC
.
${X11_INCLUDE_DIR}
${GMT_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/src/mbio)
# Install
message("set up mbaux install targets")
install(TARGETS mbaux DESTINATION cmake-example/lib)
install(TARGETS mbxgr DESTINATION cmake-example/lib)
Yet when I look in the generated Makefile I see my message "set up mbaux install targets" but there is no reference to the destination 'cmake-example/lib', and no specific 'install' target that I can see.
Thanks!
Just because you seem new to CMake, a couple words of advice re: install rules...
First, avoid hard-coding destinations. You always want to include(GNUInstallDirs) (even on Windows). This module defines a number of standard cache variables for configuring installation destinations.
See the documentation for the full list, but the following are the most relevant here:
CMAKE_INSTALL_INCLUDEDIR - the path below the installation prefix where the include structure should exist. Typically, and by default, include.
CMAKE_INSTALL_LIBDIR - the path below the installation prefix where libraries should be stored. Typically, and by default, lib. Often overridden on multiarch systems to include the target architecture, such as lib/aarch64-linux-gnu.
CMAKE_INSTALL_DATADIR - the path below the installation prefix where package-specific data should be stored. Typically, and by default, share.
In CMake 3.14+ these are used as the default destinations for install(TARGETS). This should be all you need:
include(GNUInstallDirs)
install(
TARGETS mbaux mbxgr ...
EXPORT mb-targets
RUNTIME COMPONENT mb-runtime
LIBRARY COMPONENT mb-runtime
NAMELINK_COMPONENT mb-development
ARCHIVE COMPONENT mb-development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
Don't forget to guard your include paths with $<BUILD_INTERFACE:...> in the main build (to allow INCLUDES DESTINATION above to overwrite them):
target_include_directories(
mbaux
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/mbio>"
)
You almost certainly do not want to use the ${<PACKAGE_NAME>_INCLUDE_DIRS} variables here, either. Instead use target_link_libraries with the relevant imported targets; this will set up include paths automatically.
target_link_libraries(mbaux PRIVATE X11::X11) # likely similar for GMT/GDAL
When you are required to specify a destination, you generally want to use the TYPE argument instead of DESTINATION. If you must use DESTINATION, use a project-specific cache variable as its argument; for example:
# will install headers to ${CMAKE_INSTALL_INCLUDEDIR}/**/*.h
# without mbio prefix, remove the trailing / to include mbio
# in the path
install(DIRECTORY "${PROJECT_SOURCE_DIR}/src/mbio/"
TYPE INCLUDE
COMPONENT mb-development
FILES_MATCHING PATTERN "*.h")
set(MB_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/cmake/mb"
CACHE STRING "Installation directory for CMake package files")
install(EXPORT mb-targets
DESTINATION "${MB_INSTALL_CMAKEDIR}"
NAMESPACE mb::
FILE mb-targets.cmake)
Finally, you should never read or set CMAKE_INSTALL_PREFIX from within the CMakeLists.txt file. Instead you should rely on relative paths (as done above) or the $<INSTALL_PREFIX> generator expression (in exceptional circumstances) to avoid making your installation script non-relocatable.
As pointed out by Stephen Newell, cmake spreads rules and logic across multiple files - and I now see where they are (in this case, cmake_install.cmake)
I am trying to build an example project that requires dozens of packages contained in different subdirectories, for example
/home/Olumide/src/project/Release/modules/module1/CMakeFiles/Export/lib/cmake/module1
/home/Olumide/src/project/Release/modules/module2/CMakeFiles/Export/lib/cmake/module2
/home/Olumide/src/project/Release/modules/module3/CMakeFiles/Export/lib/cmake/module3
...
Given that the application that I'm trying to build is in the location /home/Olumide/src/project/applications/tutorial, is it possible to build the application without explicitly specifying the paths to all each package that the project requires, or modifying the CMakeLists.txt file, for example by specify a common root path to be searched for all the packages, for example:
cmake -DCMAKE_MODULE_PATH=/home/Olumide/src/project/Release/modules .
Unfortunately this does not work and cmake complains that it cannot find package configuration files with the names Modul1eConfig.cmake or module1-config.cmake etc.
If all of the modules adhere tho this exact pattern you should be able to provide the "root path" via cache variable and pass a the info where to look for the libs to find_package via PATHS parameter:
# maybe a more reasonable default here?
set(MYPROJECT_MODULE_PATH "/home/Olumide/src/project/Release/modules" CACHE PATH "the path to the modules we're looking for")
set(MYPROJECT_MODULE_NAMES
module1
module2
...
)
foreach(_MODULE IN LISTS MYPROJECT_MODULE_NAMES)
find_package(${_MODULE} REQUIRED CONFIG
PATHS "${MYPROJECT_MODULE_PATH}/${_MODULE}/CMakeFiles/Export"
)
endforeach()
In general if these packages logically belong to the same software package, you should provide a way of finding those modules by passing them as components for find_package but since this seems to be code not under your own control you may not have that luxury. (E.g. boost provides this functionality: find_package(boost COMPONENTS test mpl ...).) However it may be worth some investigation if there is a configuration script somewhere that's supposed to automatically include the modules, if listed as components, e.g. somewhere in Release or Release/modules.
Build OS: Windows 10, Cmake 3.16.3.
I use target_link_libraries to link 3rd party .lib file to my .dll library.
But when I use GET_RUNTIME_DEPENDENCIES to install my dll, there is no dependency found.
It happens only on Windows, installing on Linux is ok.
Is there any clues how to solve this problem, or at least how to debug it?
What exact command uses CMake on Windows to determine dependencies?
I call GET_RUNTIME_DEPENDENCIES like this:
file(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR RES
UNRESOLVED_DEPENDENCIES_VAR UNRES
CONFLICTING_DEPENDENCIES_PREFIX CONFLICTING_DEPENDENCIES
EXECUTABLES ${EXECS}
LIBRARIES ${LIBS} ${MODULES} ${QTPLUGINS_LIBS}
DIRECTORIES ${RUNTIME_DEPENDENCIES_DIRECTORIES}
POST_EXCLUDE_REGEXES ${PLATFORM_POST_EXCLUDE_REGEXES}
)
Where LIBS contains my dll but no RES no UNRES contains path to 3rd paty dll.
So, there's a serious nastiness to all this runtime-dependency-finding magic in the newer CMakes, and it's not really their fault at all. The problem is that you, I, and roughly 90% of the rest of the CMake user world have been doing find modules wrong #THISWHOLETIME, and now our chickens have come home to roost because, as you've likely discovered, GET_RUNTIME_DEPENDENCIES / RUNTIME_DEPENDENCY_SET, $<TARGET_RUNTIME_DLLS> will all completely sh*t the bed if you try to use them with targets that have (what I now know to be) broken IMPORTED dependencies created by Find modules that don't properly set them up. So, last month I posted this screed (my earlier link) over at the CMake Discourse forum:
Windows libraries, Find modules, and TARGET_RUNTIME_DLLS
The Windows DLL Question™ has come up numerous times before in one form or another, but it’s cast in a new light by $<TARGET_RUNTIME_DLLS>, so here’s a fresh take on it.
If you’re like me (and roughly 90% of all CMake users/developers out there are like me, from what I’ve been able to observe in public projects’ source trees), your approach to writing Find modules on Windows has probably been something like this:
Use the same code on all three desktop platforms
Let CMake discover .lib / .dll.a import libraries instead of actual DLLs, using find_library().
End up creating your targets as UNKNOWN IMPORTED, because if you try to create SHARED IMPORTED library targets with only an import library it won’t work, but UNKNOWN IMPORTED works just fine so meh.
Set the import library as the target’s IMPORTED_LOCATION since that seems to work fine.
Call it a day, because hey — everything compiles.
That’s served us all for years (decades, really) so we’ve mostly just accepted it as the way CMake works on Windows.
But now along comes $<TARGET_RUNTIME_DLLS>. If you’ve tried to actually use it on Windows, you’ve probably discovered is that while all of your CONFIG-mode package dependencies’ DLLs are captured just fine, the generator expression will cheerfully ignore any targets created from a Find module that’s written like I describe above. …Which is probably most of them. (In my own library build, it was all of them, even the ones I didn’t write.)
For $<TARGET_RUNTIME_DLLS> to work, the IMPORTED target has to be correctly defined as a SHARED library target, and it needs to have its IMPORTED_ properties set correctly: import lib path in IMPORTED_IMPLIB, DLL path in IMPORTED_LOCATION.
So, now I have this new module that uses DLLTOOL.EXE and its handy -I flag to get the name of an import library’s DLL, then looks it up using find_program(). (Simply because find_library() won’t match DLLs, and I wanted to look on the PATH. I could’ve used find_file() but I’m pretty sure I’d have to explicitly give it more paths to search.)
The macro takes one argument, the name of your already-configured variable <prefix>_IMPLIB. (Or <prefix>_IMPLIBS, it’s pluralization agnostic and will follow whichever form your input uses when naming its output variable.)
The variable whose name you pass to it should already contain a valid path for an import library. Typically that’s set by find_library(), even though we’ve all been treating them like runtime libraries (DLLs) when they are not.
Armed with find_library(<prefix>_IMPLIB ...) output, implib_to_dll(<prefix>_IMPLIB) will attempt to discover and automatically populate the corresponding variable <prefix>_LIBRARY with the path to the import lib’s associated runtime DLL.
With all of the correct variables set to the correct values, it’s now possible to properly configure SHARED IMPORTED library targets on Windows. $<TARGET_RUNTIME_DLLS> can then be used to discover and operate on the set of DLLs defined by those target(s).
Kind of a pain in the Find, and really does sort of feel like something CMake could be doing at-least-semi-automatically. But, at least for now it works.
Now I just have to rewrite all of my find modules to use it. Sigh.
ImplibUtils.cmake
#[=======================================================================[.rst:
IMPLIB_UTILS
------------
Tools for CMake on WIN32 to associate IMPORTED_IMPLIB paths (as discovered
by the :command:`find_library` command) with their IMPORTED_LOCATION DLLs.
Writing Find modules that create ``SHARED IMPORTED`` targets with the
correct ``IMPORTED_IMPLIB`` and ``IMPORTED_LOCATION`` properties is a
requirement for ``$<TARGET_RUNTIME_DLLS>`` to work correctly. (Probably
``IMPORTED_RUNTIME_DEPENDENCIES`` as well.)
Macros Provided
^^^^^^^^^^^^^^^
Currently the only tool here is ``implib_to_dll``. It takes a single
argument, the __name__ (_not_ value!) of a prefixed ``<prefix>_IMPLIB``
variable (containing the path to a ``.lib`` or ``.dll.a`` import library).
``implib_to_dll`` will attempt to locate the corresponding ``.dll`` file
for that import library, and set the variable ``<prefix>_LIBRARY``
to its location.
``implib_to_dll`` relies on the ``dlltool.exe`` utility. The path can
be set by defining ``DLLTOOL_EXECUTABLE`` in the cache prior to
including this module, if it is not set implib_utils will attempt to locate
``dlltool.exe`` using ``find_program()``.
Revision history
^^^^^^^^^^^^^^^^
2021-11-18 - Updated docs to remove CACHE mentions, fixed formatting
2021-10-14 - Initial version
Author: FeRD (Frank Dana) <ferdnyc#gmail.com>
License: CC0-1.0 (Creative Commons Universal Public Domain Dedication)
#]=======================================================================]
include_guard(DIRECTORY)
if (NOT WIN32)
# Nothing to do here!
return()
endif()
if (NOT DEFINED DLLTOOL_EXECUTABLE)
find_program(DLLTOOL_EXECUTABLE
NAMES dlltool dlltool.exe
DOC "The path to the DLLTOOL utility"
)
if (DLLTOOL_EXECUTABLE STREQUAL "DLLTOOL_EXECUTABLE-NOTFOUND")
message(WARNING "DLLTOOL not available, cannot continue")
return()
endif()
message(DEBUG "Found dlltool at ${DLLTOOL_EXECUTABLE}")
endif()
#
### Macro: implib_to_dll
#
# (Win32 only)
# Uses dlltool.exe to find the name of the dll associated with the
# supplied import library.
macro(implib_to_dll _implib_var)
set(_implib ${${_implib_var}})
set(_library_var "${_implib_var}")
# Automatically update the name, assuming it's in the correct format
string(REGEX REPLACE
[[_IMPLIBS$]] [[_LIBRARIES]]
_library_var "${_library_var}")
string(REGEX REPLACE
[[_IMPLIB$]] [[_LIBRARY]]
_library_var "${_library_var}")
# We can't use the input variable name without blowing away the
# previously-discovered contents, so that's a non-starter
if ("${_implib_var}" STREQUAL "${_library_var}")
message(ERROR "Name collision! You probably didn't pass "
"implib_to_dll() a correctly-formatted variable name. "
"Only <prefix>_IMPLIB or <prefix>_IMPLIBS is supported.")
return()
endif()
if(EXISTS "${_implib}")
message(DEBUG "Looking up dll name for import library ${_implib}")
execute_process(COMMAND
"${DLLTOOL_EXECUTABLE}" -I "${_implib}"
OUTPUT_VARIABLE _dll_name
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(DEBUG "DLLTOOL returned ${_dll_name}, finding...")
# Check the directory where the import lib is found
get_filename_component(_implib_dir ".." REALPATH
BASE_DIR "${_implib}")
message(DEBUG "Checking import lib directory ${_implib_dir}")
# Add a check in ../../bin/, relative to the import library
get_filename_component(_bindir "../../bin" REALPATH
BASE_DIR "${_implib}")
message(DEBUG "Also checking ${_bindir}")
find_program(${_library_var}
NAMES ${_dll_name}
HINTS
${_bindir}
${_implib_dir}
PATHS
ENV PATH
)
set(${_library_var} "${${_library_var}}" PARENT_SCOPE)
message(DEBUG "Set ${_library_var} to ${${_library_var}}")
endif()
endmacro()
GET_RUNTIME_DEPENDENCIES isn't aware of your configure-time variables, so will you need to specify them manually. This answer states you can pass-on the variables to the install step, but I haven't been able to make it work so far. Fortunately, it does support generator expressions.
Another problem in your snippet is it must be called at install time. For example in an install(CODE ...) block.
So with all this in mind, this should get you started.
install(CODE [[
file(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR RES
UNRESOLVED_DEPENDENCIES_VAR UNRES
CONFLICTING_DEPENDENCIES_PREFIX CONFLICTING_DEPENDENCIES
EXECUTABLES $<TARGET_FILE:your_executable_target_name>
LIBRARIES $<TARGET_FILE:a_lib_target_name>
)
message("\n\nFound dependencies :")
foreach(DEP ${RES})
message("${DEP}")
endforeach()
message("\n\nNot found dependencies :")
foreach(DEP ${UNRES})
message("${DEP}")
endforeach()
]])
Build your install target to see the results.
cmake ..
cmake --build . --target install
We have a CMakeLists.txt that links (for instance) opencv to our various binaries. This is done as follow:
find_package(OpenCV REQUIRED core imgproc highgui contrib)
target_link_library(XXX opencv_core)
We also would like to allow the person building the library to provide its own opencv library. It seems that this could be done setting -DCMAKE_PREFIX_PATH to the right path.
cmake -DCMAKE_PREFIX_PATH=".../mybuild/include;.../mybuild/lib" .
However I would like to be sure the library used is exactly the one specified by the client (i.e. if there is nothing in /mybuild/lib the configuration fails).
How can I allow somebody building the library to override the library used ? (if nothing is specified it should fall back to find_package-s)
In short
If the package provides <package>Config.cmake script, user may specify <package>_DIR CMake variable for locate this script.
Searching other places in that case may be disabled with NO_DEFAULT_PATH option for find_package().
If a package is searched with Find<package>.cmake script, there is no (generic) way for disable searching other places if user provides hint variable but it is wrong.
Explanations
Firstly, CMAKE_PREFIX_PATH provides additional installation tree for all packages. As this variable is applied to all find_package() calls, it is not wise to require all packages to be found under it.
When talk about the ways for specify installation directory for specific package, we need to distinguish two kinds of "find" scripts:
<package>Config.cmake (or some alternative names, see find_package documentation).
These scripts are shipped with the packages themselves. And there is universal way for user to specify location of such packages: CMake variable <package>_DIR, which should point to the directory with *Config.cmake script.
While default behaviour of find_package() is treating <package>_DIR variable as an additional hint, passing NO_DEFAULT_PATH option disables all implicit paths:
if(<package>_DIR) # Variable is set by the user or by previous `cmake` run.
# Search only under given directory.
find_package(<package> NO_DEFAULT_PATH)
else()
# Search everywhere (as documented for 'find_package').
find_package(<package>)
endif()
Find<package>.cmake.
This script either is shipped with CMake or should be shipped with the project.
Most of such scripts allows to hint about package location with variable (CMake or environment one) like <package>_DIR, <package>_ROOT or so.
However, almost all such scripts treat hint variable only as additional search place, so if variable is set to wrong value, they simply ignore it. And without modifying the script you cannot change that behavior.