c++ - Cmake Regenerate on variable change -
y part:
i've got cmake macro runs external program , creates project specific variables. i'd have cmake run script on every build, check if variables have changed, , iff have (to prevent full project rebuild when has not), regenerate configure_file header...i'm not sure how though. (add custom target rebuilds header file every time , can't call macros, same add custom command).
x part:
so i've written following script pulling hg version information use in c++ project:
macro (readprojectrevisionstatus) exec_program(hg ${project_source_dir} args paths output_variable ${project_name}_hgpaths) message(status "${project_name}_hgpaths=${${project_name}_hgpaths}}") if (not(${project_name}_hgpaths strequal "")) string(replace "\n" ";" ${project_name}_hgpaths ${${project_name}_hgpaths}) foreach(hgpath ${${project_name}_hgpaths}) string(substring "${hgpath}" 0 10 hgpathstart) if (hgpathstart matches "default = ") string(length "${hgpath}" hgpathlength) math(expr hgsublen "${hgpathlength}-10") string(substring "${hgpath}" 10 ${hgsublen} ${project_name}_hgremotedir) endif() endforeach() endif() if (not ${project_name}_hgremotedir) message(warning "no remote repository set. use current direcoty build number, value may inaccurate.") set(${project_name}_hgremotedir ${project_source_dir}) else() exec_program(hg ${${project_name}_hgremotedir} args status return_value hgremotestatusvalue output_variable nul) if (not hgremotestatusvalue equal 0) message(warning "cannot connect remote repository @ ${${project_name}_hgremotedir}. use current direcoty build number, value may inaccurate.") set(${project_name}_hgremotedir ${project_source_dir}) endif() endif() #identify changeset exec_program(hg ${project_source_dir} args "id" "-i" output_variable output_variable ${project_name}_hghashcode) if (${project_name}_hghashcode matches ".*\\+") message(status "node dirty. generate temporary version number...") set (${project_name}_hgdirty 1) string(length ${${project_name}_hghashcode} hghashlen) math(expr hghashlen "${hghashlen}-1") string(substring ${${project_name}_hghashcode} 0 ${hghashlen} ${project_name}_hghashcode) endif() #check if remote repository contains changeset exec_program(hg ${${project_name}_hgremotedir} args "log" "-r" "${${project_name}_hghashcode}" return_value hgremotehaschangeset output_variable nul) if (not hgremotehaschangeset equal 0) message(warning "remote repository ${${project_name}_hgremotedir} not have changeset ${${project_name}_hghashcode}. use current direcoty build number, value may inaccurate.") set(${project_name}_hgremotedir ${project_source_dir}) endif() exec_program(hg ${${project_name}_hgremotedir} args "log" "-r" ${${project_name}_hghashcode} "--template" "{latesttag}" output_variable ${project_name}_hgmajorminorversion) message(status "${project_name}_hgmajorminorversion=${${project_name}_hgmajorminorversion}") exec_program(hg ${${project_name}_hgremotedir} args "log" "-r" ${${project_name}_hghashcode} "--template" "{latesttagdistance}" output_variable ${project_name}_hgbuildnumber) if (${project_name}_hgdirty) math(expr ${project_name}_hgbuildnumber "${${project_name}_hgbuildnumber}+1") set(${project_name}_hghashcode "${${project_name}_hghashcode}+") endif() message(status "version=${${project_name}_hgmajorminorversion}.${${project_name}_hgbuildnumber}.${${project_name}_hghashcode}") endmacro()
and call this.
readprojectrevisionstatus() configure_file(${cmake_current_source_dir}/main.h.in ${cmake_current_binary_dir}/main.h)
where main.h.in is:
#ifndef main_h #define main_h #include <string> const std::string ${project_name}_hgmajorminorversion = "${${project_name}_hgmajorminorversion}"; const std::string ${project_name}_hgbuildnumber = "${${project_name}_hgbuildnumber}"; const std::string ${project_name}_hghashcode = "${${project_name}_hghashcode}"; const std::string ${project_name}_hg_short_version = ${project_name}_hgmajorminorversion+"."+ ${project_name}_hgbuildnumber; const std::string ${project_name}_hg_version = ${project_name}_hg_short_version + "." + ${project_name}_hghashcode; #endif
what i'd run macro on every build command (i can use add_custom_target , set variables instead of reading them project), want regenerate main.h if variables have changed (to prevent unnecessary recompilation).
edit: working solution
made hgversion.cmake
cmake_minimum_required (version 2.8) macro (readprojectrevisionstatus) message(status project_source_dir=${project_source_dir}) exec_program(hg ${project_source_dir} args paths output_variable ${project_name}_hgpaths) message(status "${project_name}_hgpaths=${${project_name}_hgpaths}}") if (not(${project_name}_hgpaths strequal "")) string(replace "\n" ";" ${project_name}_hgpaths ${${project_name}_hgpaths}) foreach(hgpath ${${project_name}_hgpaths}) string(substring "${hgpath}" 0 10 hgpathstart) if (hgpathstart matches "default = ") string(length "${hgpath}" hgpathlength) math(expr hgsublen "${hgpathlength}-10") string(substring "${hgpath}" 10 ${hgsublen} ${project_name}_hgremotedir) endif() endforeach() endif() if (not ${project_name}_hgremotedir) message(warning "no remote repository set. use current direcoty build number, value may inaccurate.") set(${project_name}_hgremotedir ${project_source_dir}) else() exec_program(hg ${${project_name}_hgremotedir} args status return_value hgremotestatusvalue output_variable nul) if (not hgremotestatusvalue equal 0) message(warning "cannot connect remote repository @ ${${project_name}_hgremotedir}. use current direcoty build number, value may inaccurate.") set(${project_name}_hgremotedir ${project_source_dir}) endif() endif() #identify changeset exec_program(hg ${project_source_dir} args "id" "-i" output_variable output_variable ${project_name}_hghashcode) if (${project_name}_hghashcode matches ".*\\+") message(status "node dirty. generate temporary version number...") set (${project_name}_hgdirty 1) string(length ${${project_name}_hghashcode} hghashlen) math(expr hghashlen "${hghashlen}-1") string(substring ${${project_name}_hghashcode} 0 ${hghashlen} ${project_name}_hghashcode) endif() #check if remote repository contains changeset exec_program(hg ${${project_name}_hgremotedir} args "log" "-r" "${${project_name}_hghashcode}" return_value hgremotehaschangeset output_variable nul) if (not hgremotehaschangeset equal 0) message(warning "remote repository ${${project_name}_hgremotedir} not have changeset ${${project_name}_hghashcode}. use current direcoty build number, value may inaccurate.") set(${project_name}_hgremotedir ${project_source_dir}) endif() exec_program(hg ${${project_name}_hgremotedir} args "log" "-r" ${${project_name}_hghashcode} "--template" "{latesttag}" output_variable ${project_name}_hgmajorminorversion) message(status "${project_name}_hgmajorminorversion=${${project_name}_hgmajorminorversion}") exec_program(hg ${${project_name}_hgremotedir} args "log" "-r" ${${project_name}_hghashcode} "--template" "{latesttagdistance}" output_variable ${project_name}_hgbuildnumber) if (${project_name}_hgdirty) math(expr ${project_name}_hgbuildnumber "${${project_name}_hgbuildnumber}+1") set(${project_name}_hghashcode "${${project_name}_hghashcode}+") endif() message(status "version=${${project_name}_hgmajorminorversion}.${${project_name}_hgbuildnumber}.${${project_name}_hghashcode}") endmacro() message(status "getting hg version") readprojectrevisionstatus() configure_file(${project_source_dir}/main.h.in ${project_binary_dir}/main.h)
in main cmake file
add_custom_target( ${project_name}_hg_version_target depends ${cmake_current_source_dir}/main.h.in command ${cmake_command} args -dproject_source_dir=${project_source_dir} -dproject_name=${project_name} -dproject_binary_dir=${project_binary_dir} -p "${cmake_current_source_dir}/hgversion.cmake")
and
add_dependencies(${project_name} ${project_name}_hg_version_target)
works seems...real messy. got better?
if during build process determines contents of file generated, cannot generate file configure_file, because configure_file operates during configuration phase , not @ build time. design choice cmake.
in case have use add_custom_command , add_custom_target generate file during build process, found out already.
for proposed solution there might chance simplify bit. generating c/c++ header file, cmake internal parser should able automatically discover of usual source code files depend on main.h. should sufficient provide custom command add_custom_command, declares output main.h file generate. cmake should call command automatically once tries build target 1 of c/c++ files depends on it. not need custom target , manually declared dependency.
Comments
Post a Comment