From 1777bb502a3c8dbcead54b11fe403e13fe9c225c Mon Sep 17 00:00:00 2001 From: Bill Hoffman Date: Wed, 15 Oct 2008 13:56:07 -0400 Subject: BUG: 4244, add a --build option to cmake that can build projects configured by CMake --- Source/cmGlobalGenerator.cxx | 11 ++++-- Source/cmGlobalGenerator.h | 16 ++++---- Source/cmProjectCommand.cxx | 4 ++ Source/cmake.cxx | 90 ++++++++++++++++++++++++++++++++++++++++++- Source/cmake.h | 7 ++++ Source/cmakemain.cxx | 15 ++++++++ Tests/CMakeBuildTest.cmake.in | 51 ++++++++++++++++++++++++ Tests/CMakeLists.txt | 6 +++ 8 files changed, 187 insertions(+), 13 deletions(-) create mode 100644 Tests/CMakeBuildTest.cmake.in diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 39891e7..0e520d7 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1117,7 +1117,9 @@ int cmGlobalGenerator::Build( const char *makeCommandCSTR, const char *config, bool clean, bool fast, - double timeout) + double timeout, + bool verbose, + const char* extraOptions) { /** * Run an executable command and put the stdout in output. @@ -1155,7 +1157,7 @@ int cmGlobalGenerator::Build( } if (!cmSystemTools::RunSingleCommand(cleanCommand.c_str(), outputPtr, - &retVal, 0, false, timeout)) + &retVal, 0, verbose, timeout)) { cmSystemTools::SetRunCommandHideConsole(hideconsole); cmSystemTools::Error("Generator: execution of make clean failed."); @@ -1178,7 +1180,8 @@ int cmGlobalGenerator::Build( // now build std::string makeCommand = this->GenerateBuildCommand(makeCommandCSTR, projectName, - 0, target, config, false, fast); + extraOptions, target, + config, false, fast); if(output) { *output += "\nRun Build Command:"; @@ -1187,7 +1190,7 @@ int cmGlobalGenerator::Build( } if (!cmSystemTools::RunSingleCommand(makeCommand.c_str(), outputPtr, - &retVal, 0, false, timeout)) + &retVal, 0, verbose, timeout)) { cmSystemTools::SetRunCommandHideConsole(hideconsole); cmSystemTools::Error diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 90810ae..51e462d 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -105,13 +105,15 @@ public: std::string *output, const char *makeProgram, const char *config, bool clean, bool fast, - double timeout); - virtual std::string GenerateBuildCommand - (const char* makeProgram, - const char *projectName, const char* additionalOptions, - const char *targetName, - const char* config, bool ignoreErrors, bool fast); - + double timeout, bool verbose=false, + const char* extraOptions = 0); + + virtual std::string GenerateBuildCommand( + const char* makeProgram, + const char *projectName, const char* additionalOptions, + const char *targetName, + const char* config, bool ignoreErrors, bool fast); + ///! Set the CMake instance void SetCMakeInstance(cmake *cm); diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 1823b5f..870d48e 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -57,6 +57,10 @@ bool cmProjectCommand if(!this->Makefile->GetDefinition("CMAKE_PROJECT_NAME")) { this->Makefile->AddDefinition("CMAKE_PROJECT_NAME", args[0].c_str()); + this->Makefile->AddCacheDefinition + ("CMAKE_PROJECT_NAME", + args[0].c_str(), + "Value Computed by CMake", cmCacheManager::STATIC); } std::vector languages; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 9a5a5d0..fcce00d 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -36,6 +36,7 @@ # include #endif +#include #include #include #include @@ -947,7 +948,8 @@ void CMakeCommandUsage(const char* program) errorStream << "cmake bootstrap\n"; #endif - + // If you add new commands, change here, + // and in cmakemain.cxx in the options table errorStream << "Usage: " << program << " -E [command] [arguments ...]\n" << "Available commands: \n" @@ -973,6 +975,7 @@ void CMakeCommandUsage(const char* program) << " time command [args] ... - run command and return elapsed time\n" << " touch file - touch a file.\n" << " touch_nocreate file - touch a file but do not create it.\n" + << " build build_dir - build the project in build_dir.\n" #if defined(_WIN32) && !defined(__CYGWIN__) << " write_regv key value - write registry value\n" << " delete_regv key - delete registry value\n" @@ -987,6 +990,7 @@ void CMakeCommandUsage(const char* program) int cmake::ExecuteCMakeCommand(std::vector& args) { + // IF YOU ADD A NEW COMMAND, DOCUMENT IT ABOVE and in cmakemain.cxx if (args.size() > 1) { // Copy file @@ -1188,7 +1192,6 @@ int cmake::ExecuteCMakeCommand(std::vector& args) << "\n"; return ret; } - // Command to calculate the md5sum of a file else if (args[1] == "md5sum" && args.size() >= 3) { @@ -4329,3 +4332,86 @@ std::vector const& cmake::GetDebugConfigs() } return this->DebugConfigs; } + + +int cmake::Build(const std::string& dir, + const std::string& target, + const std::string& config, + const std::string& extraBuildOptions, + bool clean) +{ + if(!cmSystemTools::FileIsDirectory(dir.c_str())) + { + std::cerr << "Error: " << dir << " is not a directory\n"; + return 1; + } + std::string cachePath = dir; + cmSystemTools::ConvertToUnixSlashes(cachePath); + cmCacheManager* cachem = this->GetCacheManager(); + cmCacheManager::CacheIterator it = cachem->NewIterator(); + if(!cachem->LoadCache(cachePath.c_str())) + { + std::cerr << "Error: could not load cache\n"; + return 1; + } + if(!it.Find("CMAKE_GENERATOR")) + { + std::cerr << "Error: could find generator in Cache\n"; + return 1; + } + cmGlobalGenerator* gen = + this->CreateGlobalGenerator(it.GetValue()); + std::string output; + std::string projName; + std::string makeProgram; + if(!it.Find("CMAKE_PROJECT_NAME")) + { + std::cerr << "Error: could not find CMAKE_PROJECT_NAME in Cache\n"; + return 1; + } + projName = it.GetValue(); + if(!it.Find("CMAKE_MAKE_PROGRAM")) + { + std::cerr << "Error: could not find CMAKE_MAKE_PROGRAM in Cache\n"; + return 1; + } + makeProgram = it.GetValue(); + return gen->Build(0, dir.c_str(), + projName.c_str(), target.c_str(), + &output, + makeProgram.c_str(), + config.c_str(), clean, false, 0, true); +} + +int cmake::DoBuild(int ac, char* av[]) +{ + std::string target; + std::string config = "Debug"; + std::string extraBuildOptions; + std::string dir; + bool clean = false; + cmsys::CommandLineArguments arg; + arg.Initialize(ac, av); + typedef cmsys::CommandLineArguments argT; + arg.AddArgument("--build", argT::SPACE_ARGUMENT, &dir, + "Build a configured cmake project --build dir."); + arg.AddArgument("--target", argT::SPACE_ARGUMENT, &target, + "Specifiy the target to build," + " if missing, all targets are built."); + arg.AddArgument("--config", argT::SPACE_ARGUMENT, &config, + "Specify configuration to build" + " if missing Debug is built."); + arg.AddArgument("--extra-options", argT::SPACE_ARGUMENT, &extraBuildOptions, + "Specify extra options to pass to build program," + " for example with gmake -jN."); + arg.AddArgument("--clean", argT::NO_ARGUMENT, &clean, + "Clean before building."); + if ( !arg.Parse() ) + { + std::cerr << "Problem parsing --build arguments:\n"; + std::cerr << arg.GetHelp() << "\n"; + return 1; + } + cmake cm; + return cm.Build(dir, target, config, extraBuildOptions, clean); +} diff --git a/Source/cmake.h b/Source/cmake.h index eca9064..269db47 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -356,7 +356,14 @@ class cmake /** Display a message to the user. */ void IssueMessage(cmake::MessageType t, std::string const& text, cmListFileBacktrace const& backtrace); + // * run the --build option + static int DoBuild(int ac, char* av[]); protected: + int Build(const std::string& dir, + const std::string& target, + const std::string& config, + const std::string& extraBuildOptions, + bool clean); void InitializeProperties(); int HandleDeleteCacheVariables(const char* var); cmPropertyMap Properties; diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index 0aaf581..46a17ef 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -85,6 +85,11 @@ static const char * cmDocumentationOptions[][3] = "variables being created. If A is specified, then it will display also " "advanced variables. If H is specified, it will also display help for " "each variable."}, + {"--build dir", "Build a configured cmake tree found in dir.", + "This option will use the native build tool from the command line to" + " build the project. Other options that can be specified with this one" + " are --target, --config, --extra-options, and --clean. For complete " + "help run --build with no options."}, {"-N", "View mode only.", "Only load the cache. Do not actually run configure and generate steps."}, {"-P ", "Process script mode.", @@ -407,6 +412,7 @@ int do_cmake(int ac, char** av) bool list_help = false; bool view_only = false; bool script_mode = false; + bool build = false; std::vector args; for(int i =0; i < ac; ++i) { @@ -414,6 +420,10 @@ int do_cmake(int ac, char** av) { wiz = true; } + else if(!command && strcmp(av[i], "--build") == 0) + { + return cmake::DoBuild(ac, av); + } else if(!command && strcmp(av[i], "--system-information") == 0) { sysinfo = true; @@ -465,6 +475,11 @@ int do_cmake(int ac, char** av) args.push_back(av[i]); } } + if(build) + { + int ret = cmake::DoBuild(ac, av); + return ret; + } if(command) { int ret = cmake::ExecuteCMakeCommand(args); diff --git a/Tests/CMakeBuildTest.cmake.in b/Tests/CMakeBuildTest.cmake.in new file mode 100644 index 0000000..10abc9e --- /dev/null +++ b/Tests/CMakeBuildTest.cmake.in @@ -0,0 +1,51 @@ +# create the binary directory +make_directory("@CMAKE_BUILD_TEST_BINARY_DIR@") + +# run cmake in the binary directory +execute_process(COMMAND "@CMAKE_CMAKE_COMMAND@" + "@CMAKE_BUILD_TEST_SOURCE_DIR@" + "-G@CMAKE_TEST_GENERATOR@" + WORKING_DIRECTORY "@CMAKE_BUILD_TEST_BINARY_DIR@" + RESULT_VARIABLE RESULT) +if(RESULT) + message(FATAL_ERROR "Error running cmake command") +endif(RESULT) + +# Now use the --build option to build the project +execute_process(COMMAND "@CMAKE_CMAKE_COMMAND@" + --build "@CMAKE_BUILD_TEST_BINARY_DIR@" --config Debug + RESULT_VARIABLE RESULT) +if(RESULT) + message(FATAL_ERROR "Error running cmake --build") +endif(RESULT) + +# check for configuration types +set(CMAKE_CONFIGURATION_TYPES @CMAKE_CONFIGURATION_TYPES@) +# run the executable out of the Debug directory if there +# are configuration types +if(CMAKE_CONFIGURATION_TYPES) + set(RUN_TEST "@CMAKE_BUILD_TEST_BINARY_DIR@/Debug/COnly") +else(CMAKE_CONFIGURATION_TYPES) + set(RUN_TEST "@CMAKE_BUILD_TEST_BINARY_DIR@/COnly") +endif(CMAKE_CONFIGURATION_TYPES) +# run the test results +message("running [${RUN_TEST}]") +execute_process(COMMAND "${RUN_TEST}" RESULT_VARIABLE RESULT) +if(RESULT) + message(FATAL_ERROR "Error running test COnly") +endif(RESULT) + +# build it again with clean and only COnly target +execute_process(COMMAND "@CMAKE_CMAKE_COMMAND@" + --build "@CMAKE_BUILD_TEST_BINARY_DIR@" --config Debug + --clean --target COnly + RESULT_VARIABLE RESULT) +if(RESULT) + message(FATAL_ERROR "Error running cmake --build") +endif(RESULT) + +# run it again after clean +execute_process(COMMAND "${RUN_TEST}" RESULT_VARIABLE RESULT) +if(RESULT) + message(FATAL_ERROR "Error running test COnly after clean ") +endif(RESULT) diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 14366b3..336ccf0 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -96,6 +96,12 @@ IF(BUILD_TESTING) ADD_TEST_MACRO(ExportImport ExportImport) ADD_TEST_MACRO(Unset Unset) + SET(CMAKE_BUILD_TEST_SOURCE_DIR "${CMake_SOURCE_DIR}/Tests/COnly") + SET(CMAKE_BUILD_TEST_BINARY_DIR "${CMake_BINARY_DIR}/Tests/CMakeBuildCOnly") + CONFIGURE_FILE("${CMake_SOURCE_DIR}/Tests/CMakeBuildTest.cmake.in" + "${CMake_BINARY_DIR}/Tests/CMakeBuildTest.cmake" @ONLY) + ADD_TEST(CMakeBuildTest ${CMAKE_CMAKE_COMMAND} -P + "${CMake_BINARY_DIR}/Tests/CMakeBuildTest.cmake") # If we are running right now with a UnixMakefiles based generator, # build the "Simple" test with the ExtraGenerators, if available -- cgit v0.12