FindLibXml2 finds local library when cross compiling

I use FindLibXml2 in my CMakeLists.txt to detect wether libxml2 is available on the target system or wether it has to be built.

include(FindLibXml2)
add_library(LibXml2 INTERFACE )
if (NOT LIBXML2_FOUND)
   ... <build libxml2 from source>

It worked well for several targets, but now, I have got a new target system, where this check fails. I have got a toolchain file for the new target and I suspect, some setting might be missing or wrong in the toolchain file.

Unfortunately the documentation of FindLibXml2 (https://cmake.org/cmake/help/v3.25/module/FindLibXml2.html) does not explain, how it works and which configuration settings can be used to tweak its behavoir.

I use cmake version 3.25.2

I am just an occasional cmake user and up to now, and I did not manage to debug a cmake program, yet. :frowning:

I hope, somebody has a hint for me.

P.S. I succeded to make cmake not finding the native libcurses by setting PKG_CONFIG_LIBDIR, but it had no visible effect on finding libxml2.

First of all, you should not use include() for Find* modules and instead use find_package() as CMake sets up variables that the code may be expecting to be around. Do things work better if you do that?

Using find_package() did not change the behavoir.

Instead of waisting my time with endless trying, I started to hack a primitive CMake debugger. I hope, I will be able to analyze my problem during this week. Unfortunately, I have to take care of other hi-prio topics, so no promise.

IN case anybody is ingterested, here is my debugger:

diff -c -r cmake-3.25.2/Source/cmake.cxx cmake-3.25.2-debugger/Source/cmake.cxx
*** cmake-3.25.2/Source/cmake.cxx	2023-01-19 15:32:19.000000000 +0100
--- cmake-3.25.2-debugger/Source/cmake.cxx	2023-02-27 11:25:12.948873298 +0100
***************
*** 869,874 ****
--- 869,879 ----
                       CommandArgument::RequiresSeparator::No, SourceArgLambda },
      CommandArgument{ "-O", CommandArgument::Values::Zero,
                       IgnoreAndTrueLambda },
+     CommandArgument{ "-d", CommandArgument::Values::Zero,
+                      [](std::string const&, cmake* state) -> bool {
+ 		       state->SetDebugger(true);
+ 		       return true;
+ 		     } },
      CommandArgument{ "-B", "No build directory specified for -B",
                       CommandArgument::Values::One,
                       CommandArgument::RequiresSeparator::No, BuildArgLambda },
diff -c -r cmake-3.25.2/Source/cmake.h cmake-3.25.2-debugger/Source/cmake.h
*** cmake-3.25.2/Source/cmake.h	2023-01-19 15:32:19.000000000 +0100
--- cmake-3.25.2-debugger/Source/cmake.h	2023-02-27 11:15:22.674405159 +0100
***************
*** 498,503 ****
--- 498,505 ----
    void SetDebugFindOutputPkgs(std::string const& args);
    void SetDebugFindOutputVars(std::string const& args);
  
+   bool GetDebugger() const { return this->Debugger; }
+   void SetDebugger(bool b) { this->Debugger = b; }
    //! Do we want trace output during the cmake run.
    bool GetTrace() const { return this->Trace; }
    void SetTrace(bool b) { this->Trace = b; }
***************
*** 684,689 ****
--- 686,692 ----
    WorkingMode CurrentWorkingMode = NORMAL_MODE;
    bool DebugOutput = false;
    bool DebugFindOutput = false;
+   bool Debugger = false;
    bool Trace = false;
    bool TraceExpand = false;
    TraceFormat TraceFormatVar = TRACE_HUMAN;
diff -c -r cmake-3.25.2/Source/cmMakefile.cxx cmake-3.25.2-debugger/Source/cmMakefile.cxx
*** cmake-3.25.2/Source/cmMakefile.cxx	2023-01-19 15:32:19.000000000 +0100
--- cmake-3.25.2-debugger/Source/cmMakefile.cxx	2023-02-27 12:00:41.762641447 +0100
***************
*** 12,17 ****
--- 12,18 ----
  #include <cstring>
  #include <sstream>
  #include <utility>
+ #include <list>
  
  #include <cm/iterator>
  #include <cm/memory>
***************
*** 406,411 ****
--- 407,545 ----
    this->ExecuteCommandCallback = std::move(callback);
  }
  
+ #include <iostream>
+ 
+ static void ShowSource(const cmListFileFunction func, const std::string &FileName)
+ {
+ 	std::ifstream file(FileName);
+ 	if (!file)
+ 		fprintf(stderr, "Failed to read  %s\n", FileName.c_str());
+ 	else
+ 	{
+ 		int CurrentLine=0;
+ 		std::string SourceLine;
+ 		while (CurrentLine < func.Line() && std::getline(file, SourceLine))
+ 			CurrentLine++;
+ 		if (file)
+ 		{
+ 			printf("%s:%ld", FileName.c_str(),func.Line());
+ 			if (func.Line() < func.LineEnd())
+ 				printf("-%ld:\n",func.LineEnd());
+ 			else
+ 				printf(": ");
+ 			while (CurrentLine <= func.LineEnd() && file)
+ 			{
+ 				printf("%s\n", SourceLine.c_str());
+ 				std::getline(file, SourceLine);
+ 				CurrentLine++;
+ 			}
+ 			return;
+ 		}
+ 	}
+ 	printf("%s(", func.OriginalName().c_str());
+ 	for (const auto &arg: func.Arguments())
+ 		printf("%s ", arg.Value.c_str());
+ 	printf(")\n");
+ }
+ 
+ static std::list<std::string> splitLine(const std::string &line)
+ {
+ 	std::list<std::string> ret;
+ 
+ 	auto it = line.begin();
+ 	auto end = line.end();
+ 	while (it != end)
+ 	{
+ 		if (isspace(*it))
+ 		{
+ 			it++;
+ 			continue;
+ 		}
+ 		auto start = it;
+ 		while (it != end && !isspace(*it))
+ 			it++;
+ 		ret.emplace_back(start,it);
+ 	}
+ 	return ret;
+ }
+ 
+ void cmMakefile::Debugger(cmListFileFunction const& lff)
+ {
+ 	const auto FileName = this->StateSnapshot.GetExecutionListFile();
+ 	ShowSource(lff, FileName);
+ 	std::set<std::string> already_expanded;
+ 	for (const auto &arg:lff.Arguments())
+ 	{
+ 		auto temp = arg.Value;
+ 		if (already_expanded.find(temp) != already_expanded.end())
+ 			continue;
+ 		already_expanded.insert(temp);
+ 		auto expanded = this->ExpandVariablesInString(temp);
+ 		if (arg.Value != expanded)
+ 			printf("%s = \"%s\"\n", arg.Value.c_str(), expanded.c_str());
+ 
+ 	}
+ 
+ 	static unsigned LastStep=0;
+ 
+ 	for (;;)
+ 	{
+ 		printf("%u> ", (unsigned)this->RecursionDepth);
+ 		std::string line;
+ 		if (!std::getline(std::cin, line))
+ 		{
+ 			this->BreakLevel = 0;
+ 			return;
+ 		}
+ 
+ 		auto args=splitLine(line);
+ 		if (args.empty() && LastStep)
+ 		{
+ 			this->BreakLevel = LastStep;
+ 			return;
+ 		}
+ 
+ 		auto cmd = args.front();
+ 		args.pop_front();
+ 
+ 		if (cmd == "s")
+ 		{
+ 			this->BreakLevel = LastStep = std::numeric_limits<unsigned>::max();
+ 			return;
+ 		}
+ 		if (cmd == "n")
+ 		{
+ 			this->BreakLevel = LastStep = this->RecursionDepth;
+ 			return;
+ 		}
+ 		LastStep=0;
+ 		if (cmd == "f")
+ 		{
+ 			this->BreakLevel = this->RecursionDepth-1;
+ 			return;
+ 		}
+ 		if (cmd == "c")
+ 		{
+ 			this->BreakLevel = 0;
+ 			return;
+ 		}
+ 		if (cmd=="q")
+ 			exit(1);
+ 		if (cmd=="p")
+ 		{
+ 			for (const auto &arg:args)
+ 			{
+   				auto var = this->GetDefinition(arg);
+ 				if (var)
+ 					printf("%s=\"%s\"\n", arg.c_str(), var->c_str());
+ 				else
+ 					printf("%s not found\n", arg.c_str());
+ 			}
+ 		}
+ 	}
+ }
+ 
+ 
  bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff,
                                  cmExecutionStatus& status,
                                  cm::optional<std::string> deferId)
***************
*** 444,449 ****
--- 578,586 ----
      return false;
    }
  
+   if (this->GetCMakeInstance()->GetDebugger() &&
+       this->RecursionDepth <= this->BreakLevel)
+   	Debugger(lff);
    // Lookup the command prototype.
    if (cmState::Command command =
          this->GetState()->GetCommandByExactName(lff.LowerCaseName())) {
diff -c -r cmake-3.25.2/Source/cmMakefile.h cmake-3.25.2-debugger/Source/cmMakefile.h
*** cmake-3.25.2/Source/cmMakefile.h	2023-01-19 15:32:19.000000000 +0100
--- cmake-3.25.2-debugger/Source/cmMakefile.h	2023-02-27 11:59:16.035392037 +0100
***************
*** 713,718 ****
--- 713,720 ----
    bool ExecuteCommand(const cmListFileFunction& lff, cmExecutionStatus& status,
                        cm::optional<std::string> deferId = {});
  
+   void Debugger(cmListFileFunction const& lff);
+ 
    //! Enable support for named language, if nil then all languages are
    /// enabled.
    void EnableLanguage(std::vector<std::string> const& languages,
***************
*** 1189,1192 ****
--- 1191,1196 ----
    bool IsSourceFileTryCompile;
    mutable bool SuppressSideEffects;
    ImportedTargetScope CurrentImportedTargetScope = ImportedTargetScope::Local;
+ 
+   unsigned BreakLevel=std::numeric_limits<int>::max();
  };

There is the --trace-expand flag which does most of what you want here (just doesn’t call out the actual argument values separately).

I finally found, that FindLibXml2.cmake invokes PKG_CHECK_MODULES(PC_LIBXML QUIET libxml-2.0), which does not seem to find libxml and than invokes find_path(), passing the directories obtained from PKG_CHECK_MODULES():

find_path(LIBXML2_INCLUDE_DIR NAMES libxml/xpath.h
   HINTS
   ${PC_LIBXML_INCLUDEDIR}
   ${PC_LIBXML_INCLUDE_DIRS}
   PATH_SUFFIXES libxml2
   )
${PC_LIBXML_INCLUDEDIR} = ""
${PC_LIBXML_INCLUDE_DIRS} = ""

and find_path() returns LIBXML2_INCLUDE_DIR=“/usr/include/libxml2”. The documentation of find_path() lists a lots of CMake and environment variables, which are used. Setting CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to false looks promising, but cmake doesn’t even find make anymore, when I use this setting.

It then calls find_library():

find_library(LIBXML2_LIBRARY NAMES xml2 libxml2 libxml2_a
   HINTS
   ${PC_LIBXML_LIBDIR}
   ${PC_LIBXML_LIBRARY_DIRS}
   )
${PC_LIBXML_LIBDIR} = ""
${PC_LIBXML_LIBRARY_DIRS} = ""

which returns LIBXML2_LIBRARY=“/usr/lib/libxml2.so”.