MantisBT - CMake
View Issue Details
0009090CMakeCTestpublic2009-06-01 16:452010-12-14 18:49
Bill Vodall 
Zach Mullen 
normalmajoralways
closedfixed 
CMake-2-6 
CMake-2-8 
0009090: CTest does not handle absolute paths in CTestTestfile SUBDIR( ) entries.

Testing enabled CMake builds where the binary directory is outside of the source tree result in SUBDIR entries in the CTestTestfile.cmake file that have absolute paths. The SUBDIR() handling in CTest always assumes the path is relative thus it fails to find and follow the subdir links with absolute paths.

A small change to ctest/cmCTestTestHandler.cxx appears to have fixed the issue both on Linux and Win32

--- ctest/cmCTestTestHandler.cxx ---

bool cmCTestSubdirCommand
::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
{
 ...
 std::vector<std::string>::const_iterator it;
 std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
 for ( it = args.begin(); it != args.end(); ++ it )
   {
   cmSystemTools::ChangeDirectory(cwd.c_str());
   std::string fname = cwd;
   fname += "/";
   fname += *it;

* if ( !cmSystemTools::FileIsDirectory(fname.c_str()) )
* {
* fname = *it;
* }

-----

Here is a slightly trimmed copy of Tylers posting to the CMAKE discussion list
documenting how to reproduce this issue.


On Fri, May 29, 2009 at 10:58:50AM -0700, Bill V wrote:
> The subdir code in CMAKE proper (cmSubdirCommand.cxx) has logic to
> handle relative or absolute paths. The subdir handling code in
> CTest/cmCTestTestHandler.cxx always assumes it's a relative path and
> prepends the current directory. Maybe cmCTestTestHandler.cxx needs
> to be updated.


Indeed. After a lot of experimenting, I think I have a small case that
demonstrates the problem.

[tylermr@alta:~/ctest-paths-test2]$ find
.
./module1
./module1/CMakeLists.txt

./module1/module1subdir
./module1/module1subdir/CMakeLists.txt

./module2
./module2/CMakeLists.txt

Module1 is the "main" project. It does add_subdirectory() on module2
(which lives next to it on disk) and module1subdir (which lives
underneath module1 on disk).

The Lists are also simple:

MODULE1
-------
project(module1)
cmake_minimum_required(VERSION 2.6)

enable_testing()

set (PLATFORM "linux")
set (BUILD_TYPE "debug")

# The source path of the subdirectory is relative to the source path of
# this project. The binary path of the subdirectory is relative to the
# *binary* path of this project, hence the extra "../".
add_subdirectory(../module2 ../../module2/${PLATFORM}/${BUILD_TYPE})
add_subdirectory(module1subdir ../module1subdir/${PLATFORM}/${BUILD_TYPE})

MODULE1SUBDIR
-------------
project(module1subdir)
cmake_minimum_required(VERSION 2.6)

add_test (module1subdirtest1 "pwd")

MODULE2
-------
project(module2)
cmake_minimum_required(VERSION 2.6)

add_test (module2test1 "pwd")



So let's do an out-of-source build on this project:

[tylermr@alta:~/ctest-paths-test2/module1]$ mkdir build
[tylermr@alta:~/ctest-paths-test2/module1]$ cd build/
[tylermr@alta:~/ctest-paths-test2/module1/build]$ cmake ..
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/lib64/ccache/gcc
-- Check for working C compiler: /usr/lib64/ccache/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/lib64/ccache/c++
-- Check for working CXX compiler: /usr/lib64/ccache/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to:
/alta/tylermr/ctest-paths-test2/module1/build


And let's inspect the CTestTestfile.cmake which is written out:

[tylermr@alta:~/ctest-paths-test2/module1/build]$ cat CTestTestfile.cmake
# CMake generated Testfile for
# Source directory: /alta/tylermr/ctest-paths-test2/module1
# Build directory: /alta/tylermr/ctest-paths-test2/module1/build
#
# This file replicates the SUBDIRS() and ADD_TEST() commands from the
# source
# tree CMakeLists.txt file, skipping any SUBDIRS() or ADD_TEST()
# commands
# that are excluded by CMake control structures, i.e. IF() commands.
SUBDIRS(/alta/tylermr/ctest-paths-test2/module2/linux/debug)
SUBDIRS(../module1subdir/linux/debug)


Note that module2 is written out with an absolute path while
module1subdir is written out with a relative path. CTest will only work
with the relative path because, as Bill noted, CTest *always* prepends
the current binary directory to the beginning of each path it finds in a
SUBDIRS() command. As a result, CTest can only find one of the test
cases in the project:

[tylermr@alta:~/ctest-paths-test2/module1/build]$ ctest
Start processing tests
Test project /alta/tylermr/ctest-paths-test2/module1/build
 1/ 1 Testing module1subdirtest1 Passed

100% tests passed, 0 tests failed out of 1


Proof of the path prepending can be seen in this excerpt from strace:

[tylermr@alta:~/ctest-paths-test2/module1/build]$ strace ctest
execve("/usr/local/bin/ctest", ["ctest"], [/* 89 vars */]) = 0
[...]
getcwd("/alta/tylermr/ctest-paths-test2/module1/build", 2048) = 46
chdir("/alta/tylermr/ctest-paths-test2/module1/build") = 0
access("/alta/tylermr/ctest-paths-test2/module1/build//alta/tylermr/ctest-paths-test2/module2/linux/debug", R_OK) = -1 ENOENT (No such file or directory)
getcwd("/alta/tylermr/ctest-paths-test2/module1/build", 2048) = 46
chdir("/alta/tylermr/ctest-paths-test2/module1/build") = 0
access("/alta/tylermr/ctest-paths-test2/module1/build/../module1subdir/linux/debug", R_OK) = 0
[...]


I think the this inconsistency stems from some logic in
cmLocalGenerator::ConvertToRelativePath (which is called by
cmLocalGenerator::Convert which is called by
cmLocalGenerator::GenerateTestFiles which is the method that writes out
CTestTestfile.cmake). I haven't read the code too closely but these
comments seem enlightening:

 // Skip conversion if the path and local are not both in the source
 // or both in the binary tree.

and

 // If no part of the path is in common then return the full path.


I believe there is a bug in CTest's SUBDIRS() command. It should accept
absolute paths, like add_subdirectory() does.

...

Btw, there is a discrepancy between how subdirs works in CTest vs how it
works in CMake. Compare CTest/cmCTestTestHandler::cmCTestSubdirCommand
with cmSubdirCommand::cmSubdirCommand.

The one for CMake has some logic that checks if
Makefile->GetCurrentDirectory/directory_from_subdir_command points
somewhere useful. If it does, cmSubdirCommand adds the directory with
the prepended current directory. (If not, it returns
directory_from_subdir_command undecorated.)

cmCTestSubdirCommand doesn't have this logic; it always prepends
GetCurrentWorkingDirectory() with no tests to make sure the resulting
decorated directory exists on the filesystem.


Thanks,
tyler
No tags attached.
Issue History
2009-06-01 16:45Bill VodallNew Issue
2009-09-30 09:54Bill HoffmanStatusnew => assigned
2009-09-30 09:54Bill HoffmanAssigned To => Zach Mullen
2009-11-09 14:10Zach MullenNote Added: 0018371
2009-11-24 14:25Zach MullenStatusassigned => resolved
2009-11-24 14:25Zach MullenFixed in Version => CMake-2-8
2009-11-24 14:25Zach MullenResolutionopen => fixed
2010-12-14 18:49David ColeNote Added: 0024070
2010-12-14 18:49David ColeStatusresolved => closed

Notes
(0018371)
Zach Mullen   
2009-11-09 14:10   
I just committed your patch. Thanks =)
(0024070)
David Cole   
2010-12-14 18:49   
Closing bugs that have been resolved for more than 3 months without any further updates.