find_library: Fix repeat call after changing directory content (#15293)

We use cmGlobalGenerator::GetDirectoryContent to avoid repeating
directory listings.  However, GetDirectoryContent loads content from
disk at most once.  This breaks find_library calls that occur when disk
content has changed since preceding find_library calls.

Teach cmGlobalGenerator::GetDirectoryContent to save the directory
modification time when content is loaded and re-load content if it
changes.

Create a RunCMake.find_library test with a case covering this.
This commit is contained in:
Brad King 2014-12-08 15:12:51 -05:00
parent 09498b2ead
commit ce331bab92
7 changed files with 55 additions and 20 deletions

View File

@ -2725,7 +2725,9 @@ void cmGlobalGenerator::AddToManifest(const std::string& config,
// Add to the content listing for the file's directory. // Add to the content listing for the file's directory.
std::string dir = cmSystemTools::GetFilenamePath(f); std::string dir = cmSystemTools::GetFilenamePath(f);
std::string file = cmSystemTools::GetFilenameName(f); std::string file = cmSystemTools::GetFilenameName(f);
this->DirectoryContentMap[dir].insert(file); DirectoryContent& dc = this->DirectoryContentMap[dir];
dc.Generated.insert(file);
dc.All.insert(file);
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -2733,25 +2735,32 @@ std::set<std::string> const&
cmGlobalGenerator::GetDirectoryContent(std::string const& dir, bool needDisk) cmGlobalGenerator::GetDirectoryContent(std::string const& dir, bool needDisk)
{ {
DirectoryContent& dc = this->DirectoryContentMap[dir]; DirectoryContent& dc = this->DirectoryContentMap[dir];
if(needDisk && !dc.LoadedFromDisk) if(needDisk)
{ {
// Load the directory content from disk. long mt = cmSystemTools::ModifiedTime(dir);
cmsys::Directory d; if (mt != dc.LastDiskTime)
if(d.Load(dir))
{ {
unsigned long n = d.GetNumberOfFiles(); // Reset to non-loaded directory content.
for(unsigned long i = 0; i < n; ++i) dc.All = dc.Generated;
// Load the directory content from disk.
cmsys::Directory d;
if(d.Load(dir))
{ {
const char* f = d.GetFile(i); unsigned long n = d.GetNumberOfFiles();
if(strcmp(f, ".") != 0 && strcmp(f, "..") != 0) for(unsigned long i = 0; i < n; ++i)
{ {
dc.insert(f); const char* f = d.GetFile(i);
if(strcmp(f, ".") != 0 && strcmp(f, "..") != 0)
{
dc.All.insert(f);
}
} }
} }
dc.LastDiskTime = mt;
} }
dc.LoadedFromDisk = true;
} }
return dc; return dc.All;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -255,9 +255,9 @@ public:
cmTargetManifest const& GetTargetManifest() const cmTargetManifest const& GetTargetManifest() const
{ return this->TargetManifest; } { return this->TargetManifest; }
/** Get the content of a directory. Directory listings are loaded /** Get the content of a directory. Directory listings are cached
from disk at most once and cached. During the generation step and re-loaded from disk only when modified. During the generation
the content will include the target files to be built even if step the content will include the target files to be built even if
they do not yet exist. */ they do not yet exist. */
std::set<std::string> const& GetDirectoryContent(std::string const& dir, std::set<std::string> const& GetDirectoryContent(std::string const& dir,
bool needDisk = true); bool needDisk = true);
@ -486,13 +486,14 @@ private:
virtual const char* GetBuildIgnoreErrorsFlag() const { return 0; } virtual const char* GetBuildIgnoreErrorsFlag() const { return 0; }
// Cache directory content and target files to be built. // Cache directory content and target files to be built.
struct DirectoryContent: public std::set<std::string> struct DirectoryContent
{ {
typedef std::set<std::string> derived; long LastDiskTime;
bool LoadedFromDisk; std::set<std::string> All;
DirectoryContent(): LoadedFromDisk(false) {} std::set<std::string> Generated;
DirectoryContent(): LastDiskTime(-1) {}
DirectoryContent(DirectoryContent const& dc): DirectoryContent(DirectoryContent const& dc):
derived(dc), LoadedFromDisk(dc.LoadedFromDisk) {} LastDiskTime(dc.LastDiskTime), All(dc.All), Generated(dc.Generated) {}
}; };
std::map<std::string, DirectoryContent> DirectoryContentMap; std::map<std::string, DirectoryContent> DirectoryContentMap;

View File

@ -104,6 +104,7 @@ add_RunCMake_test(export)
add_RunCMake_test(cmake_minimum_required) add_RunCMake_test(cmake_minimum_required)
add_RunCMake_test(continue) add_RunCMake_test(continue)
add_RunCMake_test(file) add_RunCMake_test(file)
add_RunCMake_test(find_library)
add_RunCMake_test(find_package) add_RunCMake_test(find_package)
add_RunCMake_test(get_filename_component) add_RunCMake_test(get_filename_component)
add_RunCMake_test(if) add_RunCMake_test(if)

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.1)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@ -0,0 +1,2 @@
CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/Created-build/lib/libcreated.a'

View File

@ -0,0 +1,16 @@
list(APPEND CMAKE_FIND_LIBRARY_PREFIXES lib)
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .a)
find_library(CREATED_LIBRARY
NAMES created
PATHS ${CMAKE_CURRENT_BINARY_DIR}/lib
NO_DEFAULT_PATH
)
message("CREATED_LIBRARY='${CREATED_LIBRARY}'")
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib/libcreated.a" "created")
find_library(CREATED_LIBRARY
NAMES created
PATHS ${CMAKE_CURRENT_BINARY_DIR}/lib
NO_DEFAULT_PATH
)
message("CREATED_LIBRARY='${CREATED_LIBRARY}'")

View File

@ -0,0 +1,3 @@
include(RunCMake)
run_cmake(Created)