├── cmake ├── vcproj2cmake │ ├── define_mappings.txt │ ├── include_mappings.txt │ └── dependency_mappings.txt └── Modules │ ├── vcproj2cmake_defs.cmake │ └── vcproj2cmake_func.cmake ├── samples ├── project_exclude_list.txt ├── hook_post.txt └── hook_project.txt ├── doc └── development_hints.txt ├── scripts ├── vcproj2cmake_settings.rb ├── vcproj2cmake_recursive.rb └── vcproj2cmake.rb └── README.txt /cmake/vcproj2cmake/define_mappings.txt: -------------------------------------------------------------------------------- 1 | _SECDLL:WIN32 2 | _WINDOWS:UNUSED__MANAGED_BY_CMAKE 3 | WIN32:UNUSED__MANAGED_BY_CMAKE 4 | WINVER=0x500:UNUSED__GLOBAL_DEFINES 5 | -------------------------------------------------------------------------------- /samples/project_exclude_list.txt: -------------------------------------------------------------------------------- 1 | .*\bintegration-test\b.* 2 | .*\bModuleTest\b.* 3 | .*\bunittest\b.* 4 | .*\bUnitTest\b.* 5 | drivers.* 6 | AwfulPlatformProj.* 7 | -------------------------------------------------------------------------------- /doc/development_hints.txt: -------------------------------------------------------------------------------- 1 | For a clean and very useful cross-platform method to do library symbol exports/imports 2 | (a macro-based approach to wrap Windows __declspec(dllexport) 3 | and gcc __attribute__ ((visibility("default"))) support), 4 | please see http://gcc.gnu.org/wiki/Visibility 5 | -------------------------------------------------------------------------------- /cmake/vcproj2cmake/include_mappings.txt: -------------------------------------------------------------------------------- 1 | .*/Extern/Boost.*:=${Boost_INCLUDE_DIRS} 2 | .*/Extern/cairo/include/cairo:WIN32|UNIX=${Cairo_INCLUDE_DIRS} 3 | .*/Extern/curl/include:=${CURL_INCLUDE_DIRS} 4 | .*/Extern/iconv/include:WIN32|UNIX=${ICONV_INCLUDE_DIRS} 5 | .*/Extern/libxml2/include/libxml2:WIN32|UNIX=${LIBXML2_INCLUDE_DIR} 6 | .*/Extern/Microsoft.*:WIN32 7 | -------------------------------------------------------------------------------- /samples/hook_post.txt: -------------------------------------------------------------------------------- 1 | # some nice code at the very end of the main (root) project, 2 | # to collect all Find operations that did not succeed. 3 | 4 | get_property(pkg_not_found GLOBAL 5 | PROPERTY PACKAGES_NOT_FOUND) 6 | 7 | if(pkg_not_found) 8 | # hmm, perhaps make use of FeatureSummary.cmake? 9 | message("Note: some packages have not been found: ${pkg_not_found}") 10 | endif(pkg_not_found) 11 | -------------------------------------------------------------------------------- /scripts/vcproj2cmake_settings.rb: -------------------------------------------------------------------------------- 1 | # user-modifiable file containing common vcproj2cmake settings, included by all vcproj2cmake scripts. 2 | 3 | # local config directory as created in every project which needs specific settings 4 | # (possibly required in root project space only) 5 | $v2c_config_dir_local = "./cmake/vcproj2cmake" 6 | 7 | # directory where local CMake modules reside, filename case is not really standardized, 8 | # thus you might want to tweak this setting 9 | $v2c_module_path_local = "./cmake/Modules" 10 | -------------------------------------------------------------------------------- /samples/hook_project.txt: -------------------------------------------------------------------------------- 1 | # might want to include some "default settings" for your entire project hierarchy 2 | #include(MyProjectDefaults) 3 | 4 | SET(Boost_ADDITIONAL_VERSIONS "1.36.0" "1.37.0") 5 | find_package(Boost REQUIRED "1.36.0" COMPONENTS system thread) 6 | # both not really needed, since the dependency/include mappings should already 7 | # automatically guarantee proper use of these variables in the converted CMakeLists.txt file. 8 | #include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) 9 | #list(APPEND V2C_LIBS ${LTDL_LIBRARIES}) 10 | -------------------------------------------------------------------------------- /cmake/Modules/vcproj2cmake_defs.cmake: -------------------------------------------------------------------------------- 1 | # reset common variables used by all converted CMakeLists.txt files 2 | set(V2C_LIBS ) 3 | set(V2C_LIB_DIRS ) 4 | set(V2C_SOURCES ) 5 | 6 | set(V2C_LOCAL_CONFIG_DIR ./cmake/vcproj2cmake CACHE STRING "Relative path to vcproj2cmake-specific content, located within every sub-project") 7 | 8 | # Add a filter variable for someone to customize in case he/she doesn't want 9 | # a rebuild somewhere for some reason (such as having multiple builds 10 | # operate simultaneously on a single source tree, 11 | # thus fiddling with source tree content during build would be a big No-No 12 | # in such case). 13 | option(V2C_USE_AUTOMATIC_CMAKELISTS_REBUILDER "Automatically rebuild converted CMakeLists.txt files upon updates on .vcproj side?" ON) 14 | 15 | # Pre-define hook include filenames 16 | # (may be redefined/overridden by local content!) 17 | set(V2C_HOOK_PROJECT ${V2C_LOCAL_CONFIG_DIR}/hook_project.txt) 18 | set(V2C_HOOK_PROJECT ${V2C_LOCAL_CONFIG_DIR}/hook_project.txt) 19 | set(V2C_HOOK_POST_SOURCES ${V2C_LOCAL_CONFIG_DIR}/hook_post_sources.txt) 20 | set(V2C_HOOK_POST_DEFINITIONS ${V2C_LOCAL_CONFIG_DIR}/hook_post_definitions.txt) 21 | set(V2C_HOOK_POST_TARGET ${V2C_LOCAL_CONFIG_DIR}/hook_post_target.txt) 22 | set(V2C_HOOK_POST ${V2C_LOCAL_CONFIG_DIR}/hook_post.txt) 23 | -------------------------------------------------------------------------------- /cmake/vcproj2cmake/dependency_mappings.txt: -------------------------------------------------------------------------------- 1 | ########################################################################### 2 | # Windows libraries (please always reference as lower-case name in original .vcproj) 3 | advapi32:WIN32|UNIX=${WIN32_LIBRARIES} 4 | comdlg32:WIN32|UNIX=${WIN32_LIBRARIES} 5 | ddraw:WIN32|UNIX=${WIN32_LIBRARIES} 6 | gdi32:WIN32|UNIX=${WIN32_LIBRARIES} 7 | gdiplus:WIN32|UNIX=${WIN32_LIBRARIES} 8 | glu32:=${OPENGL_glu_LIBRARY} 9 | htmlhelp:WIN32|UNIX=${WIN32_LIBRARIES} 10 | kernel32:WIN32|UNIX=${WIN32_LIBRARIES} 11 | mfcs80:WIN32|UNIX=${WIN32_LIBRARIES} 12 | mfcs80d:WIN32|UNIX=${WIN32_LIBRARIES} 13 | mpr:WIN32|UNIX=${WIN32_LIBRARIES} 14 | msimg32:WIN32|UNIX=${WIN32_LIBRARIES} 15 | msvcrt:WIN32|UNIX=${WIN32_LIBRARIES} 16 | nddeapi:WIN32|UNIX=${WIN32_LIBRARIES} 17 | netapi32:WIN32|UNIX=${WIN32_LIBRARIES} 18 | odbc32:WIN32|UNIX=${ODBC_LIBRARIES} 19 | odbccp32:WIN32|UNIX=${ODBC_LIBRARIES} 20 | ole32:WIN32|UNIX=${WIN32_LIBRARIES} 21 | oleaut32:WIN32|UNIX=${WIN32_LIBRARIES} 22 | opengl32:=${OPENGL_gl_LIBRARY} 23 | rpcrt4:WIN32|UNIX=${WIN32_LIBRARIES} 24 | setupapi:WIN32|UNIX=${WIN32_LIBRARIES} 25 | shell32:WIN32|UNIX=${WIN32_LIBRARIES} 26 | user32:WIN32|UNIX=${WIN32_LIBRARIES} 27 | uuid:WIN32|UNIX=${WIN32_LIBRARIES} 28 | version:WIN32|UNIX=${WIN32_LIBRARIES} 29 | wininet:WIN32|UNIX=${WIN32_LIBRARIES} 30 | winmm:WIN32|UNIX=${WIN32_LIBRARIES} 31 | winspool:WIN32|UNIX=${WIN32_LIBRARIES} 32 | ########################################################################### 33 | # third-party libraries 34 | dynapdf:WIN32 35 | ########################################################################### 36 | # translate "Internet project" components into their related Findxxx.cmake 37 | # macro's ${xxx_LIBRARIES} string. 38 | cairo:=${Cairo_LIBRARIES} 39 | iconv:=${ICONV_LIBRARIES} 40 | libboost_filesystem-vc80-mt-gd-1_34:=${Boost_LIBRARIES} 41 | libboost_filesystem-vc80-mt-1_34:=${Boost_LIBRARIES} 42 | libboost_regex-vc80-mt-gd-1_34:=${Boost_LIBRARIES} 43 | libboost_regex-vc80-mt-1_34:=${Boost_LIBRARIES} 44 | libboost_signals-vc80-mt-gd-1_34:=${Boost_LIBRARIES} 45 | libboost_signals-vc80-mt-1_34:=${Boost_LIBRARIES} 46 | libboost_system-vc80-mt-gd-1_34:=${Boost_LIBRARIES} 47 | libboost_system-vc80-mt-1_34:=${Boost_LIBRARIES} 48 | libboost_thread-vc80-mt-gd-1_34:=${Boost_LIBRARIES} 49 | libboost_thread-vc80-mt-1_34:=${Boost_LIBRARIES} 50 | libboost_wserialization-vc80-mt-gd-1_34:=${Boost_LIBRARIES} 51 | libboost_wserialization-vc80-mt-1_34:=${Boost_LIBRARIES} 52 | libcurl:=${CURL_LIBRARIES} 53 | libpng:=${PNG_LIBRARIES} 54 | libxml2:=${LIBXML2_LIBRARIES} 55 | zdll:=${ZLIB_LIBRARIES} 56 | ########################################################################### 57 | # Application-specific libraries 58 | # foo library is Win32-only 59 | foo:WIN32 60 | ########################################################################### 61 | # translation of project-specific libraries from their debug-postfix _names_ to actual _target_ expression 62 | bar:WIN32 63 | bar_d:WIN32=bar 64 | baz_u:WIN32=baz 65 | baz_ud:WIN32=baz 66 | barf_d:=barf 67 | -------------------------------------------------------------------------------- /cmake/Modules/vcproj2cmake_func.cmake: -------------------------------------------------------------------------------- 1 | # This vcproj2cmake-specific CMake module should be available 2 | # at least in your root project (i.e., PROJECT/cmake/Modules/) 3 | 4 | # Some helper functions to be used by all converted projects in the tree 5 | 6 | # avoid useless repeated parsing 7 | if(V2C_FUNC_DEFINED) 8 | return() 9 | endif(V2C_FUNC_DEFINED) 10 | set(V2C_FUNC_DEFINED true) 11 | 12 | 13 | # Function to automagically rebuild our converted CMakeLists.txt 14 | # by the original converter script in case any relevant files changed. 15 | function(v2c_rebuild_on_update _target_name _vcproj _cmakelists _script _master_proj_dir) 16 | if(V2C_USE_AUTOMATIC_CMAKELISTS_REBUILDER) 17 | message(STATUS "${_target_name}: installing ${_cmakelists} rebuilder (watching ${_vcproj})") 18 | find_program(v2c_ruby NAMES ruby) 19 | if(NOT v2c_ruby) 20 | message("could not detect your ruby installation (perhaps forgot to set CMAKE_PREFIX_PATH?), aborting: won't automagically rebuild CMakeLists.txt on changes...") 21 | return() 22 | endif(NOT v2c_ruby) 23 | # there are some uncertainties about how to locate the ruby script. 24 | # for now, let's just hardcode a "must have been converted from root project" requirement. 25 | ## canonicalize script, to be able to precisely launch it via a CMAKE_SOURCE_DIR root dir base 26 | #file(RELATIVE_PATH _script_rel "${CMAKE_SOURCE_DIR}" "${_script}") 27 | ##message(FATAL_ERROR "_script ${_script} _script_rel ${_script_rel}") 28 | # need an intermediate stamp file, otherwise "make clean" will clean 29 | # our live output file (CMakeLists.txt), yet we need to preserve it 30 | # since it hosts this very CMakeLists.txt rebuilder... 31 | set(stamp_file "${CMAKE_CURRENT_BINARY_DIR}/cmakelists_rebuilder.stamp") 32 | # add dependencies for mappings files in both root project and current project 33 | # FIXME: should obey V2C_LOCAL_CONFIG_DIR setting!! 34 | set(mappings_files "cmake/vcproj2cmake/*_mappings.txt") 35 | file(GLOB mappings "${CMAKE_SOURCE_DIR}/${mappings_files}") 36 | list(APPEND v2c_mappings ${mappings}) 37 | file(GLOB mappings "${mappings_files}") 38 | list(APPEND v2c_mappings ${mappings}) 39 | #message("v2c_mappings ${v2c_mappings}") 40 | add_custom_command(OUTPUT "${stamp_file}" 41 | COMMAND ${v2c_ruby} ${_script} ${_vcproj} ${_cmakelists} ${_master_proj_dir} 42 | COMMAND "${CMAKE_COMMAND}" -E touch "${stamp_file}" 43 | # FIXME add any other relevant dependencies here 44 | DEPENDS ${_vcproj} ${_script} ${v2c_mappings} 45 | COMMENT "vcproj settings changed, rebuilding ${_cmakelists}" 46 | VERBATIM 47 | ) 48 | # TODO: do we have to set_source_files_properties(GENERATED) on ${_cmakelists}? 49 | 50 | # NOTE: we use update_cmakelists_[TARGET] names instead of [TARGET]_... 51 | # since in certain IDEs these peripheral targets will end up as user-visible folders 52 | # and we want to keep them darn out of sight via suitable sorting! 53 | set(target_update_cmakelists update_cmakelists_${_target_name}) 54 | #add_custom_target(${target_update_cmakelists} DEPENDS ${_cmakelists} VERBATIM) 55 | add_custom_target(${target_update_cmakelists} ALL VERBATIM DEPENDS "${stamp_file}" VERBATIM) 56 | 57 | if(TARGET ${_target_name}) # in some projects an actual target might not exist (i.e. we simply got passed the project name) 58 | # make sure the rebuild happens _before_ trying to build the actual target. 59 | add_dependencies(${_target_name} ${target_update_cmakelists}) 60 | endif(TARGET ${_target_name}) 61 | # and have an update_cmakelists_ALL target to be able to update all 62 | # outdated CMakeLists.txt files within a project hierarchy 63 | if(NOT TARGET update_cmakelists_ALL) 64 | add_custom_target(update_cmakelists_ALL) 65 | endif(NOT TARGET update_cmakelists_ALL) 66 | add_dependencies(update_cmakelists_ALL ${target_update_cmakelists}) 67 | 68 | # FIXME!!: we should definitely achieve aborting build process directly 69 | # after a new CMakeLists.txt has been generated (we don't want to go 70 | # full steam ahead with _old_ CMakeLists.txt content), 71 | # however I don't quite know yet how to hook up those targets 72 | # to actually get it to work. 73 | # ok, well, in fact ideally processing should be aborted after _all_ sub projects 74 | # have been converted, but _before_ any of these progresses towards building. 75 | # Which is even harder to achieve, I guess... (set a marker variable 76 | # or marker file and check for it somewhere global at the end of it all, 77 | # then abort, that would be the idea) 78 | # add_custom_target(update_cmakelists_abort_build ALL 79 | ## COMMAND /bin/false 80 | # COMMAND sdfgsdf 81 | # DEPENDS "${_cmakelists}" 82 | # VERBATIM 83 | # ) 84 | # 85 | # add_dependencies(update_cmakelists_abort_build update_cmakelists) 86 | endif(V2C_USE_AUTOMATIC_CMAKELISTS_REBUILDER) 87 | endfunction(v2c_rebuild_on_update _target_name _vcproj _cmakelists _script _master_proj_dir) 88 | -------------------------------------------------------------------------------- /scripts/vcproj2cmake_recursive.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'fileutils' 4 | require 'find' 5 | require 'pathname' 6 | 7 | # load common settings 8 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 9 | load 'vcproj2cmake_settings.rb' 10 | 11 | script_fqpn = File.expand_path $0 12 | script_path = Pathname.new(script_fqpn).parent 13 | source_root = Dir.pwd 14 | 15 | if not File.exist?($v2c_config_dir_local) 16 | FileUtils.mkdir_p $v2c_config_dir_local 17 | end 18 | 19 | time_cmake_root_folder = 0 20 | arr_excl_proj = Array.new() 21 | time_cmake_root_folder = File.stat($v2c_config_dir_local).mtime.to_i 22 | excluded_projects = "#{$v2c_config_dir_local}/project_exclude_list.txt" 23 | if File.exist?(excluded_projects) 24 | f_excl = File.new(excluded_projects, 'r') 25 | f_excl.each do |line| 26 | # TODO: we probably need a per-platform implementation, 27 | # since exclusion is most likely per-platform after all 28 | arr_excl_proj.push(line) 29 | end 30 | f_excl.close 31 | end 32 | 33 | # FIXME: should _split_ operation between _either_ scanning entire .vcproj hierarchy into a 34 | # all_sub_projects.txt, _or_ converting all sub .vcproj as listed in an existing all_sub_projects.txt file. 35 | # (provide suitable command line switches) 36 | # Hmm, or perhaps port _everything_ back into vcproj2cmake.rb, 37 | # providing --recursive together with --scan or --convert switches for all_sub_projects.txt generation or use. 38 | 39 | projlistfile = File.new("#{$v2c_config_dir_local}/all_sub_projects.txt", "w+") 40 | 41 | Find.find('./') do 42 | |f| 43 | # skip symlinks since they might be pointing _backwards_! 44 | next if FileTest.symlink?(f) 45 | if test(?d, f) 46 | # skip CMake build directories! (containing CMake-generated .vcproj files!) 47 | # FIXME: more precise checking: check file _content_ against CMake generation! 48 | is_excluded = false 49 | if f =~ /^build/i 50 | is_excluded = true 51 | else 52 | arr_excl_proj.each do |excluded| 53 | excl_regex = "^\.\/#{excluded.chomp}$" 54 | #puts "MATCH: #{f} vs. #{excl_regex}" 55 | if f =~ /#{excl_regex}/ 56 | is_excluded = true 57 | break 58 | end 59 | end 60 | end 61 | #puts "excluded: #{is_excluded}" 62 | if is_excluded == true 63 | puts "EXCLUDED #{f}!" 64 | next 65 | end 66 | puts "processing #{f}!" 67 | dir = Dir.open(f) 68 | #puts "entries: #{dir.entries}" 69 | if (!dir.entries.grep(/^CMakeLists.txt$/i).empty?) 70 | #puts dir.entries 71 | #puts "CMakeLists.txt exists in #{f}, checking!" 72 | f_cmakelists = File.new("#{f}/CMakeLists.txt", "r") 73 | auto_generated = f_cmakelists.grep(/AUTO-GENERATED by/) 74 | f_cmakelists.close 75 | if (auto_generated.empty?) 76 | puts "existing #{f}/CMakeLists.txt is custom, \"native\" form --> skipping!" 77 | next 78 | else 79 | # ok, it _is_ a CMakeLists.txt, but a temporary vcproj2cmake one 80 | # which we can overwrite. 81 | puts "existing #{f}/CMakeLists.txt is our own auto-generated file --> replacing!" 82 | end 83 | end 84 | files = dir.entries.grep(/\.vcproj$/i) 85 | #puts files 86 | 87 | # in each directory, find the .vcproj file to use. 88 | # Prefer xxx_vc8.vcproj, but in cases of directories where this is 89 | # not available, use a non-_vc8 file. 90 | projfile = nil 91 | files.each do |file| 92 | if file =~ /_vc8.vcproj$/i 93 | # ok, we found a _vc8 version, quit searching since this is what we prefer 94 | projfile = file 95 | break 96 | end 97 | if file =~ /.vcproj$/i 98 | projfile = file 99 | # do NOT break here (another _vc8 file might come along!) 100 | end 101 | end 102 | #puts "projfile is #{projfile}" 103 | if projfile.nil? 104 | else 105 | f_vcproj = File.new("#{f}/#{projfile}", "r") 106 | cmakelists_text = f_vcproj.grep(/CMakeLists.txt/) 107 | f_vcproj.close 108 | if (cmakelists_text.empty?) 109 | if projfile =~ /_vc8.vcproj/i 110 | else 111 | puts "Darn, no _vc8.vcproj in #{f}! Should have offered one..." 112 | end 113 | # verify age of .vcproj file... (NOT activated: experimental feature!) 114 | rebuild = 0 115 | if File.exist?("#{f}/CMakeLists.txt") 116 | # is .vcproj newer (or equal: let's rebuild copies with flat timestamps!) 117 | # than CMakeLists.txt? 118 | # NOTE: if we need to add even more dependencies here, then it 119 | # might be a good idea to do this stuff properly and use a CMake-based re-build 120 | # infrastructure instead... 121 | # FIXME: doesn't really seem to work... yet? 122 | time_proj = File.stat("#{f}/#{projfile}").mtime.to_i 123 | time_cmake_folder = 0 124 | if File.exist?("#{f}/#{$v2c_config_dir_local}") 125 | time_cmake_folder = File.stat("#{f}/#{$v2c_config_dir_local}").mtime.to_i 126 | end 127 | time_CMakeLists = File.stat("#{f}/CMakeLists.txt").mtime.to_i 128 | #puts "TIME: CMakeLists #{time_CMakeLists} proj #{time_proj} cmake_folder #{time_cmake_folder} cmake_root_folder #{time_cmake_root_folder}" 129 | if time_proj > time_CMakeLists 130 | #puts "modified: project!" 131 | rebuild = 1 132 | elsif time_cmake_folder > time_CMakeLists 133 | #puts "modified: cmake/!" 134 | rebuild = 1 135 | elsif time_cmake_root_folder > time_CMakeLists 136 | #puts "modified: cmake/ root!" 137 | rebuild = 1 138 | end 139 | else 140 | # no CMakeLists.txt at all, definitely process this project 141 | rebuild = 2 142 | end 143 | if rebuild > 0 144 | #puts "REBUILD #{f}!! #{rebuild}" 145 | end 146 | #puts "#{f}/#{file}" 147 | # see "A dozen (or so) ways to start sub-processes in Ruby: Part 1" 148 | puts "launching ruby #{script_path}/vcproj2cmake.rb '#{f}/#{projfile}' '#{f}/CMakeLists.txt' '#{source_root}'" 149 | output = `ruby #{script_path}/vcproj2cmake.rb '#{f}/#{projfile}' '#{f}/CMakeLists.txt' '#{source_root}'` 150 | puts "output was:" 151 | puts "#{output}" 152 | 153 | # the root directory is special: it might contain another project (it shouldn't!!), 154 | # thus we need to skip it if so (then include the root directory 155 | # project by placing a CMakeLists_native.txt there and have it include the 156 | # auto-generated CMakeLists.txt) 157 | if not f == "./" 158 | if f.include? ' ' # quote strings containing spaces!! 159 | projlistfile.puts("add_subdirectory( \"#{f}\" )") 160 | else 161 | projlistfile.puts("add_subdirectory( #{f} )") 162 | end 163 | end 164 | #output.split("\n").each do |line| 165 | # puts "[parent] output: #{line}" 166 | #end 167 | #puts 168 | else 169 | puts "Skipping CMake-generated file #{f}/#{projfile}" 170 | end 171 | end 172 | end 173 | end 174 | 175 | projlistfile.close 176 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | vcproj2cmake.rb - .vcproj to CMakeLists.txt converter scripts 2 | written by Jesper Eskilson and Andreas Mohr. 3 | FIXME licensing (BSD) 4 | 5 | ---------- 6 | DISCLAIMER: there are NO WARRANTIES as to the suitability of this converter, 7 | thus make sure to have suitable backup; if things break, 8 | then you certainly get to keep both parts. 9 | ---------- 10 | 11 | 12 | vcproj2cmake has been fully tested with CMake 2.6.x only - while some 2.8.x testing has been done, 13 | there may be some rough spots. 14 | 15 | 16 | Usage (very rough summary), with Linux/Makefile generator: 17 | - use existing Visual Studio project source tree which contains a .vcproj file 18 | - in the project source tree, run ruby [PATH_TO_VCPROJ2CMAKE]/scripts/vcproj2cmake.rb PROJECT.vcproj 19 | (alternatively, execute vcproj2cmake_recursive.rb to convert an entire hierarchy of .vcproj sub projects) 20 | - copy all required cmake/Modules, cmake/vcproj2cmake and samples (provided by the vcproj2cmake source tree!) 21 | to their respective paths in your project source tree 22 | - after successfully converting the .vcproj file to a CMakeLists.txt, start your out-of-tree CMake builds: 23 | - mkdir ../[PROJECT_NAME].build_toolkit1_v1.2.3_unicode_debug 24 | - cd ../[PROJECT_NAME].build_toolkit1_v1.2.3_unicode_debug 25 | - cmake ../[PROJECT_NAME] (alternatively: ccmake ../[PROJECT_NAME]) 26 | - time make -j3 -k 27 | 28 | 29 | NOTE: first thing to state is: 30 | if you do not have any users who are hooked on keep using 31 | their static .vcproj files on Visual Studio, then it perhaps makes less sense 32 | to use our converter as a somewhat more cumbersome _online converter_ solution 33 | - instead you may choose to go for a full-scale manual conversion 34 | to CMakeLists.txt files (by basing your initial CMakeLists.txt layout 35 | on the output of our script, too, of course). 36 | That way you can avoid having to deal with the hook script includes as 37 | required by our online conversion concept, and instead modify your 38 | CMakeLists.txt files directly wherever needed (since _they_ will be your 39 | authoritative project information in future on all platforms, instead of the static .vcproj files). 40 | 41 | OTOH by using our scripts for one-time-conversion only, you will lose out 42 | on any of the hopefully substantial improvements done to our 43 | online conversion script in the future 44 | (such as automagically provided installation/packaging configuration mechanisms, ...), 45 | thus it's a tough initial decision to make on whether to maintain an online conversion 46 | infrastructure or to go initial-convert only and thus run _all_ sides on a CMake-based 47 | setup. 48 | 49 | 50 | 51 | =============================================================================== 52 | Explanation of core concepts: 53 | 54 | 55 | === Hook script includes === 56 | 57 | In the generated CMakeLists.txt file(s), you may notice lines like 58 | include(${V2C_HOOK_PROJECT} OPTIONAL) 59 | These are meant to provide interception points ("hooks") to enhance online-converted 60 | CMakeLists.txt with specific static content (e.g. to call required CMake Find scripts 61 | via "find_package(Foobar REQUIRED)", 62 | or to override some undesireable .vcproj choices, to provide some user-facing 63 | CMake setup cache variables, etc.). 64 | One could just as easily have written this line like 65 | include(cmake/vcproj2cmake/hook_project.txt OPTIONAL) 66 | , but then it would be somewhat less flexible (some environments might want to 67 | temporarily disable use of these included scripts, by changing the variable 68 | to a different/inexistent script). 69 | Note that these required variables like V2C_HOOK_PROJECT are pre-defined by our 70 | vcproj2cmake_defs.cmake module. 71 | 72 | 73 | Example hook scripts to be used by every sub project in your project hierarchy that needs 74 | such customizations are provided in our repository's sample/ directory. 75 | 76 | 77 | === mappings files (definitions, dependencies, library directories, include directories) === 78 | 79 | Certain compiler defines in your projects may be Win32-only, 80 | and certain other defines might need a different replacement on a certain other platform. 81 | 82 | Dito with library dependencies, and especially with include and library directories. 83 | 84 | This is what vcproj2cmake's mappings file mechanism is meant to solve 85 | (see cmake/vcproj2cmake/include_mappings.txt etc.). 86 | 87 | 88 | Basic syntax of mappings files is: 89 | 90 | Original expression as used by the static Windows side (.vcproj content) 91 | - note case sensitivity! -, 92 | then ':' as separator between original content and CMake-side mappings, 93 | then a platform-specific identifier (WIN32, APPLE, ...) which is used 94 | in a CMake "if(...)" conditional (or no identifier in case the mapping 95 | is supposed to be platform-universal), 96 | then a '=' to assign the replacement expression to be used on that platform, 97 | then the ensuing replacement expression. 98 | Then an '|' (pipe, "or") for an optional series of additional platform conditionals. 99 | 100 | 101 | Note that ideally you merely need to centrally maintain all mappings in your root project part 102 | (ROOT_PROJECT/cmake/vcproj2cmake/*_mappings.txt), since sub projects will also 103 | collect information from the root project in addition to their (optional) local mappings files. 104 | 105 | 106 | === Miscellaneous === 107 | 108 | vcproj2cmake_recursive.rb supports skipping of certain unwanted sub projects 109 | (e.g. ones that are very cross-platform incompatible) within your 110 | Visual Studio project tree. 111 | This is to be done by mentioning the names of the projects to be excluded 112 | in the file $v2c_config_dir_local/project_exclude_list.txt 113 | 114 | 115 | === Automatic re-conversion upon .vcproj changes === 116 | 117 | vcproj2cmake now contains a mechanism for _automatically_ triggered re-conversion of files 118 | whenever the backing .vcproj file received some updates. 119 | This is implemented in function 120 | cmake/Modules/vcproj2cmake_func.cmake/v2c_rebuild_on_update() 121 | This internal mechanism is enabled by default, you may modify the CMake cache variable 122 | V2C_USE_AUTOMATIC_CMAKELISTS_REBUILDER to disable it. 123 | NOTE: in order to have the automatic re-conversion mechanism work properly, 124 | this currently needs the initial (manual) converter invocation 125 | to be done from root project, _not_ any other directory (FIXME get rid of this limitation). 126 | 127 | Since the converter will be re-executed from within the generated files (Makefile etc.), 128 | it will convert the CMakeLists.txt that these are based on _within_ this same process. 129 | However, it has no means to abort subsequent target execution once it notices that there 130 | were .vcproj changes which render the current CMake generator build files obsolete. 131 | Thus, the current build instance will run to its end, and it's important to then launch 132 | it a second time to have CMake start a new configure run with the CMakeLists.txt and 133 | then re-build all newly modified targets. 134 | There's no appreciable way to immediately re-build the updated configuration - 135 | see CMake list "User-accessible hook on internal cmake_check_build_system target?". 136 | 137 | To cleanly re-convert all CMakeLists.txt in an isolated way after a source upgrade via SCM, 138 | you may invoke target update_cmakelists_ALL, followed by doing a full build. 139 | 140 | 141 | === Troubleshooting === 142 | 143 | - use CMake's message(FATAL_ERROR "DBG: xxx") command 144 | - add_custom_command(... COMMENT="DBG: we are doing ${THIS} and failing ${THAT}") 145 | - cmake --debug-output --trace 146 | 147 | If there's compile failure due to missing includes, then this probably means that 148 | the newly converted CMakeLists.txt still contains an include_directories() command 149 | which lists some paths in their raw, original, Windows-specific form. 150 | What should have happened is automatic replacement of such path strings 151 | with a CMake-side configuration variable (e.g. ${toolkit_INCLUDE_DIR}) 152 | via a regular expression in the mappings file (include_mappings.txt). 153 | Then CMake will consult the setting at ${toolkit_INCLUDE_DIR} 154 | (which should have been gathered during a CMake configure run, probably 155 | within one of the vcproj2cmake hook scripts that are explained above). 156 | 157 | If things appear to be failing left and right, 158 | the reason might be a lack of CMake proficiency, thus it's perhaps best 159 | to start with a new small CMake sample project 160 | (perhaps use one of the samples on the internet) before using this converter, 161 | to gain some CMake experience (CMake itself has a rather steep learning curve, 162 | thus it might be even worse trying to start with an Alpha-stage 163 | .vcproj to CMake converter). 164 | 165 | 166 | === Installation/packaging === 167 | 168 | Installation/packaging of a vcproj2cmake-based project is not specially supported yet, 169 | however I'm currently in the process of setting packaging up locally, 170 | thus hopefully this will eventually result in a nicely generic, easily usable 171 | (and optionally overridable!) mechanism which provides a nice Bundle-like 172 | packaging functionality on all platforms (Mac _and_ Linux, and Windows etc.). 173 | 174 | I just finished packaging, but in my case all I had to do was to use 175 | GetPrerequisites.cmake on the main project target (i.e., the main executable), 176 | this listed all sub project targets already and allowed me to install them 177 | from a global configuration part. 178 | Thus there's no special per-project vcproj2cmake handling yet. 179 | 180 | 181 | === Related projects, alternative setups === 182 | 183 | sln2mak (.sln to Makefile converter), http://www.codeproject.com/KB/cross-platform/sln2mak.aspx 184 | 185 | I just have to state that we have a very unfair advantage here: 186 | while this script implementation possibly might be better 187 | than our converter (who knows...), the fact that we are converting 188 | towards CMake (and thus to a whole universe of supported build environments 189 | /IDEs via CMake's generators) probably renders any shortcomings 190 | that we might have rather very moot. 191 | 192 | 193 | A completely alternative way of gaining cross-platform builds 194 | other than making use of CMake via vcproj2cmake 195 | may be to stay within proprietary .vcproj / Visual Studio realms 196 | and to implement a cross-compiler setup 197 | - this is said to be doable, and perhaps it can even be preferrable (would be nice 198 | to receive input in case anyone has particular experience in this area). 199 | 200 | 201 | === Off-Topic parts === 202 | 203 | If someone is still making use of the SCM (Source Control Management) 204 | abomination called VSS 205 | and contemplating migrating to a different, actually usable system, 206 | then it may be useful to NOT default-decide to go for the "obvious" successor 207 | (Microsoft TFS), but instead making an Informed Decision (tm) of which 208 | capable SCM (or in fact, an integrated Tracker/Ticket environment [ALM]) to choose. 209 | While TFS is an awful lot better than VSS, it still has some painful 210 | shortcomings, among these: 211 | - no three-way-merges via common base version, i.e. base-less merge 212 | http://jamesmckay.net/2011/01/baseless-merges-in-team-foundation-server-why/ 213 | - no disconnected SCM operation (server connection required) 214 | - installation is a veritable PITA 215 | 216 | For a very revealing discussion with lots of experienced SCM/ALM people, 217 | you may look at 218 | http://jamesmckay.net/2011/02/team-foundation-server-is-the-lotus-notes-of-version-control-tools/ 219 | 220 | In short, it is strongly advisable to also check out other (possibly much more 221 | transparently developed) ALMs such as Trac before committing to a specific product 222 | (these environments make up a large part of your development inner loop, 223 | thus a wrong choice will cost dearly in wasted time and inefficiency). 224 | http://almatters.wordpress.com/2010/08/19/alm-open-source-tools-eclipse-mylyn-subclipse-trac-subversion/ 225 | 226 | 227 | While git might be an obvious cross-platform SCM candidate, Windows integration of Mercurial 228 | probably is somewhat better. 229 | Useful URLs: 230 | http://mercurial.selenic.com/wiki/SourceSafeConversion 231 | http://code.google.com/p/vss2git/ 232 | http://code.google.com/p/gitextensions/ 233 | 234 | 235 | 236 | === Epilogue === 237 | 238 | Whenever something needs better explanation, just tell me and I'll try to improve it. 239 | Dito if you think that some mechanism is poorly implemented (we're still at pre-Beta stage!). 240 | 241 | Despite being at Alpha stage, the converter is now more than usable enough 242 | to successfully build a very large project consisting of several dozen sub projects. 243 | 244 | Happy hacking, 245 | 246 | Andreas Mohr 247 | -------------------------------------------------------------------------------- /scripts/vcproj2cmake.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | # Given a Visual Studio project, create a CMakeLists.txt file which optionally 4 | # allows for ongoing side-by-side operation (e.g. on Linux, Mac) 5 | # together with the existing static .vcproj project on the Windows side. 6 | # Provides good support for simple DLL/Static/Executable projects, 7 | # but custom build steps and build events are ignored. 8 | 9 | # Author: Jesper Eskilson 10 | # Email: jesper [at] eskilson [dot] se 11 | # Author 2: Andreas Mohr 12 | # Email: andi [at] lisas [period] de 13 | # Large list of extensions: 14 | # list _all_ configuration types, add indenting, add per-platform configuration 15 | # of definitions, dependencies and includes, add optional includes 16 | # to provide static content, thus allowing for a nice on-the-fly 17 | # generation mode of operation _side-by-side_ existing and _updated_ .vcproj files, 18 | # fully support recursive handling of all .vcproj file groups (filters). 19 | 20 | # If you add code, please try to keep this file generic and modular, 21 | # to enable other people to hook into a particular part easily 22 | # and thus keep any additions specific to the requirements of your local project _separate_. 23 | 24 | # TODO/NOTE: 25 | # Always make sure that a simple vcproj2cmake.rb run will result in a 26 | # fully working _self-contained_ CMakeLists.txt, no matter how small 27 | # the current vcproj2cmake config environment is 28 | # (i.e., it needs to work even without a single specification file) 29 | 30 | # TODO: 31 | # - perhaps there's a way to provide more precise/comfortable hook script handling? 32 | # - should continue with clean separation of .vcproj content parsing and .vcproj output 33 | # generation (e.g. in preparation for .vcxproj support) 34 | # And move everything into classes (not sure about the extent of Ruby 35 | # support here). Create vcproj parser class(es) which works on a 36 | # common parser support base class, feed it some vcproj configuration 37 | # class, configure that class, then push it (with the vcproj settings 38 | # it contains) over to a CMake generator class. 39 | # - try to come up with an ingenious way to near-_automatically_ handle those pesky repeated 40 | # dependency requirements of several sub projects 41 | # (e.g. the component-based Boost Find scripts, etc.) instead of having to manually 42 | # write custom hook script content (which cannot be kept synchronized 43 | # with changes _automatically_!!) each time due to changing components and libraries. 44 | 45 | require 'fileutils' 46 | require 'tempfile' 47 | require 'pathname' 48 | require 'rexml/document' 49 | include FileUtils::Verbose 50 | 51 | # load common settings 52 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 53 | load 'vcproj2cmake_settings.rb' 54 | 55 | # Usage: vcproj2cmake.rb [] [] 56 | 57 | #******************************************************************************************************* 58 | # Check for command-line input errors 59 | # ----------------------------------- 60 | cl_error = "" 61 | 62 | script_name = $0 63 | 64 | if ARGV.length < 1 65 | cl_error = "*** Too few arguments\n" 66 | else 67 | 68 | vcproj_filename = ARGV.shift 69 | #puts "First arg is #{vcproj_filename}" 70 | 71 | # Discovered Ruby 1.8.7(?) BUG: kills extension on duplicate slashes: ".//test.ext" 72 | # OK: ruby-1.8.5-5.el5_4.8, KO: u10.04 ruby1.8 1.8.7.249-2 and ruby1.9.1 1.9.1.378-1 73 | # http://redmine.ruby-lang.org/issues/show/3882 74 | # TODO: add a version check to conditionally skip this cleanup effort? 75 | vcproj_filename = Pathname.new(vcproj_filename).cleanpath 76 | 77 | if File.extname(vcproj_filename) != ".vcproj" 78 | # The first argument on the command-line did not have a '.vcproj' extension. 79 | # If the local directory contains file "ARGV[0].vcproj" then use it, else error. 80 | # (Note: Only '+' works here for concatenation, not '<<'.) 81 | vcproj_filename = vcproj_filename + ".vcproj" 82 | 83 | #puts "Looking for #{vcproj_filename}" 84 | unless FileTest.exist?(vcproj_filename) 85 | cl_error = "*** The first argument must be the Visual Studio project name\n" 86 | end 87 | end 88 | end 89 | 90 | if ARGV.length > 3 91 | cl_error = cl_error << "*** Too many arguments\n" 92 | end 93 | 94 | unless cl_error == "" 95 | puts "" 96 | puts "*** Input Error *** #{script_name}" 97 | puts "#{cl_error}" 98 | puts "" 99 | puts "Usage: vcproj2cmake.rb [] []" 100 | 101 | exit 102 | end 103 | 104 | # Process the optional command-line arguments 105 | # ------------------------------------------- 106 | # FIXME: Variables 'output_file' and 'master_project_dir' are position-dependent on the 107 | # command-line, if they are entered. The script does not have a way to distinguish whether they 108 | # were input in the wrong order. A potential fix is to associate flags with the arguments, like 109 | # '-i [-o ] [-d ]' and then parse 110 | # them accordingly. This lets them be entered in any order and removes ambiguity. 111 | # ------------------------------------------- 112 | output_file = ARGV.shift or output_file = File.join(File.dirname(vcproj_filename), "CMakeLists.txt") 113 | 114 | # Master (root) project dir defaults to current dir--useful for simple, single-.vcproj conversions. 115 | $master_project_dir = ARGV.shift 116 | if not $master_project_dir 117 | $master_project_dir = "." 118 | end 119 | #******************************************************************************************************* 120 | 121 | ### USER-CONFIGURABLE SECTION ### 122 | 123 | # since the .vcproj multi-configuration environment has some settings 124 | # that can be specified per-configuration (target type [lib/exe], include directories) 125 | # but where CMake unfortunately does _NOT_ offer a configuration-specific equivalent, 126 | # we need to fall back to using the globally-scoped CMake commands (include_directories() etc.). 127 | # But at least let's optionally allow the user to precisely specify which configuration 128 | # (empty [first config], "Debug", "Release", ...) he wants to have 129 | # these settings taken from. 130 | config_multi_authoritative = "" 131 | 132 | filename_map_inc = "#{$v2c_config_dir_local}/include_mappings.txt" 133 | filename_map_def = "#{$v2c_config_dir_local}/define_mappings.txt" 134 | filename_map_dep = "#{$v2c_config_dir_local}/dependency_mappings.txt" 135 | filename_map_lib_dirs = "#{$v2c_config_dir_local}/lib_dirs_mappings.txt" 136 | 137 | $myindent = 0 138 | 139 | ### USER-CONFIGURABLE SECTION END ### 140 | 141 | 142 | p_vcproj = Pathname.new(vcproj_filename) 143 | # figure out a global project_dir variable from the .vcproj location 144 | project_dir = p_vcproj.dirname 145 | 146 | #p_project_dir = Pathname.new(project_dir) 147 | #p_cmakelists = Pathname.new(output_file) 148 | #cmakelists_dir = p_cmakelists.dirname 149 | #p_cmakelists_dir = Pathname.new(cmakelists_dir) 150 | #p_cmakelists_dir.relative_path_from(...) 151 | 152 | 153 | # monster HACK: set a global variable, since we need to be able 154 | # to tell whether we're able to build a target 155 | # (i.e. whether we have any build units i.e. 156 | # implementation files / non-header files), 157 | # otherwise we should not add a target since CMake will 158 | # complain with "Cannot determine link language for target "xxx"". 159 | $have_build_units = false 160 | 161 | 162 | # global variable to indicate whether we want debug output or not 163 | $debug = false 164 | 165 | def puts_debug(str) 166 | if $debug 167 | puts str 168 | end 169 | end 170 | 171 | def puts_ind(chan, str) 172 | chan.print ' ' * $myindent 173 | chan.puts str 174 | end 175 | 176 | # tiny helper, simply to save some LOC 177 | def new_puts_ind(chan, str) 178 | chan.puts 179 | puts_ind(chan, str) 180 | end 181 | 182 | # Change \ to /, and remove leading ./ 183 | def normalize(p) 184 | felems = p.gsub("\\", "/").split("/") 185 | # DON'T eradicate single '.'!! 186 | felems.shift if felems[0] == "." and felems.size > 1 187 | File.join(felems) 188 | end 189 | 190 | def escape_char(in_string, esc_char) 191 | #puts "in_string #{in_string}" 192 | in_string.gsub!(/#{esc_char}/, "\\#{esc_char}") 193 | #puts "in_string quoted #{in_string}" 194 | end 195 | 196 | def escape_backslash(in_string) 197 | # "Escaping a Backslash In Ruby's Gsub": "The reason for this is that 198 | # the backslash is special in the gsub method. To correctly output a 199 | # backslash, 4 backslashes are needed.". Oerks - oh well, do it. 200 | # hrmm, seems we need some more even... 201 | # (or could we use single quotes (''') for that? Too lazy to retry...) 202 | in_string.gsub!(/\\/, "\\\\\\\\") 203 | end 204 | 205 | def read_mappings(filename_mappings, mappings) 206 | # line format is: "tag:PLATFORM1:PLATFORM2=tag_replacement2:PLATFORM3=tag_replacement3" 207 | if File.exists?(filename_mappings) 208 | #Hash[*File.read(filename_mappings).scan(/^(.*)=(.*)$/).flatten] 209 | File.open(filename_mappings, 'r').each do |line| 210 | next if line =~ /^\s*#/ 211 | b, c = line.chomp.split(/:/) 212 | mappings[b] = c 213 | end 214 | else 215 | puts_debug "NOTE: #{filename_mappings} NOT AVAILABLE" 216 | end 217 | #puts mappings["kernel32"] 218 | #puts mappings["mytest"] 219 | end 220 | 221 | def read_mappings_combined(filename_mappings, mappings) 222 | if $master_project_dir 223 | # read common mappings to be used by all sub projects 224 | read_mappings("#{$master_project_dir}/#{filename_mappings}", mappings) 225 | end 226 | read_mappings(filename_mappings, mappings) 227 | end 228 | 229 | def push_platform_def(platform_defs, platform, def_value) 230 | #puts "adding #{def_value} on platform #{platform}" 231 | if platform_defs[platform].nil? 232 | platform_defs[platform] = Array.new 233 | end 234 | platform_defs[platform].push(def_value) 235 | end 236 | 237 | def parse_platform_conversions(platform_defs, arr_defs, map_defs) 238 | arr_defs.each { |curr_value| 239 | #puts map_defs[curr_value] 240 | map_line = map_defs[curr_value] 241 | if map_line.nil? 242 | # hmm, no direct match! Try to figure out whether any map entry 243 | # is a regex which would match our curr_value 244 | map_defs.each do |key, value| 245 | if curr_value =~ /^#{key}$/ 246 | puts_debug "KEY: #{key} curr_value #{curr_value}" 247 | map_line = value 248 | break 249 | end 250 | end 251 | end 252 | if map_line.nil? 253 | # no mapping? --> unconditionally use the original define 254 | push_platform_def(platform_defs, "ALL", curr_value) 255 | else 256 | map_line.chomp.split(/\|/).each do |platform_element| 257 | #puts "platform_element #{platform_element}" 258 | platform, replacement_def = platform_element.split(/=/) 259 | if platform.empty? 260 | # specified a replacement without a specific platform? 261 | # ("tag:=REPLACEMENT") 262 | # --> unconditionally use it! 263 | platform = "ALL" 264 | else 265 | if replacement_def.nil? 266 | replacement_def = curr_value 267 | end 268 | end 269 | push_platform_def(platform_defs, platform, replacement_def) 270 | end 271 | end 272 | } 273 | end 274 | 275 | def cmake_write_build_attributes(cmake_command, element_prefix, out, arr_defs, map_defs, cmake_command_arg) 276 | # the container for the list of _actual_ dependencies as stated by the project 277 | all_platform_defs = Hash.new 278 | parse_platform_conversions(all_platform_defs, arr_defs, map_defs) 279 | all_platform_defs.each { |key, arr_platdefs| 280 | #puts "arr_platdefs: #{arr_platdefs}" 281 | next if arr_platdefs.empty? 282 | arr_platdefs.uniq! 283 | out.puts 284 | specific_platform = !(key.eql?("ALL")) 285 | if specific_platform 286 | puts_ind(out, "if(#{key})") 287 | $myindent += 2 288 | end 289 | if cmake_command_arg.nil? 290 | puts_ind(out, "#{cmake_command}(") 291 | else 292 | puts_ind(out, "#{cmake_command}(#{cmake_command_arg}") 293 | end 294 | arr_platdefs.each do |curr_value| 295 | puts_ind(out, " #{element_prefix}#{curr_value}") 296 | end 297 | puts_ind(out, ")") 298 | if specific_platform 299 | $myindent -= 2 300 | puts_ind(out, "endif(#{key})") 301 | end 302 | } 303 | end 304 | 305 | def cmake_get_config_name_upcase(config_name) 306 | # need to also convert config names with spaces into underscore variants, right? 307 | config_name.clone.upcase.gsub(/ /,'_') 308 | end 309 | 310 | def cmake_set_target_property(target, property, value, out) 311 | puts_ind(out, "set_property(TARGET #{target} PROPERTY #{property} \"#{value}\")") 312 | end 313 | 314 | def cmake_set_target_property_compile_definitions(target, config_name, arr_defs, map_defs, out) 315 | config_name_upper = cmake_get_config_name_upcase(config_name) 316 | # the container for the list of _actual_ dependencies as stated by the project 317 | all_platform_defs = Hash.new 318 | parse_platform_conversions(all_platform_defs, arr_defs, map_defs) 319 | all_platform_defs.each { |key, arr_platdefs| 320 | #puts "arr_platdefs: #{arr_platdefs}" 321 | next if arr_platdefs.empty? 322 | arr_platdefs.uniq! 323 | out.puts 324 | specific_platform = !(key.eql?("ALL")) 325 | if specific_platform 326 | puts_ind(out, "if(#{key})") 327 | $myindent += 2 328 | end 329 | # make sure to specify APPEND for greater flexibility (hooks etc.) 330 | puts_ind(out, "set_property(TARGET #{target} APPEND PROPERTY COMPILE_DEFINITIONS_#{config_name_upper}") 331 | arr_platdefs.each do |curr_value| 332 | puts_ind(out, " #{curr_value}") 333 | end 334 | puts_ind(out, ")") 335 | if specific_platform 336 | $myindent -= 2 337 | puts_ind(out, "endif(#{key})") 338 | end 339 | } 340 | end 341 | 342 | def cmake_set_target_property_compile_flags(target, config_name, arr_flags, out) 343 | return if arr_flags.empty? 344 | config_name_upper = cmake_get_config_name_upcase(config_name) 345 | # original compiler flags are MSVC-only, of course. TODO: provide an automatic conversion towards gcc? 346 | new_puts_ind(out, "if(MSVC)") 347 | puts_ind(out, "set_property(TARGET #{target} APPEND PROPERTY COMPILE_FLAGS_#{config_name_upper} ") 348 | arr_flags.each do |curr_value| 349 | puts_ind(out, " #{curr_value}") 350 | end 351 | puts_ind(out, ")") 352 | puts_ind(out, "endif(MSVC)") 353 | end 354 | 355 | def vc8_parse_file(project, file, arr_sources) 356 | projname = project.attributes["Name"] 357 | f = normalize(file.attributes["RelativePath"]) 358 | 359 | ## Ignore header files 360 | #return if f =~ /\.(h|H|lex|y|ico|bmp|txt)$/ 361 | # No we should NOT ignore header files: if they aren't added to the target, 362 | # then VS won't display them in the file tree. 363 | return if f =~ /\.(lex|y|ico|bmp|txt)$/ 364 | 365 | 366 | # Ignore files which have the ExcludedFromBuild attribute set to TRUE 367 | excluded_from_build = false 368 | file.elements.each("FileConfiguration") { |file_config| 369 | #file_config.elements.each('Tool[@Name="VCCLCompilerTool"]') { |compiler| 370 | # if compiler.attributes["UsePrecompiledHeader"] 371 | #} 372 | excl_build = file_config.attributes["ExcludedFromBuild"] 373 | if not excl_build.nil? and excl_build.downcase == "true" 374 | excluded_from_build = true 375 | return # no complex handling, just return 376 | end 377 | } 378 | 379 | # Ignore files with custom build steps 380 | included_in_build = true 381 | file.elements.each("FileConfiguration/Tool") { |tool| 382 | if tool.attributes["Name"] == "VCCustomBuildTool" 383 | included_in_build = false 384 | return # no complex handling, just return 385 | end 386 | } 387 | 388 | # Verbosely ignore IDL generated files 389 | if f =~/_(i|p).c$/ 390 | # see file_mappings.txt comment above 391 | puts "#{projname}::#{f} is an IDL generated file: skipping! FIXME: should be platform-dependent." 392 | included_in_build = false 393 | return # no complex handling, just return 394 | end 395 | 396 | # Verbosely ignore .lib "sources" 397 | if f =~ /\.lib$/ 398 | # probably these entries are supposed to serve as dependencies 399 | # (i.e., non-link header-only include dependency, to ensure 400 | # rebuilds in case of foreign-library header file changes). 401 | # Not sure whether these were added by users or 402 | # it's actually some standard MSVS mechanism... FIXME 403 | puts "#{projname}::#{f} registered as a \"source\" file!? Skipping!" 404 | included_in_build = false 405 | return # no complex handling, just return 406 | end 407 | 408 | if not excluded_from_build and included_in_build 409 | arr_sources.push(f) 410 | if f =~ /\.(c|C)/ 411 | $have_build_units = true 412 | end 413 | end 414 | end 415 | 416 | Files_str = Struct.new(:name, :arr_sub_filters, :arr_files) 417 | 418 | def vc8_get_config_name(config) 419 | config.attributes["Name"].split("|")[0] 420 | end 421 | 422 | def vc8_get_configuration_types(project, configuration_types) 423 | project.elements.each("Configurations/Configuration") { |config| 424 | config_name = vc8_get_config_name(config) 425 | configuration_types.push(config_name) 426 | } 427 | end 428 | 429 | # analogous to CMake separate_arguments() command 430 | def cmake_separate_arguments(array_in) 431 | array_in.join(";") 432 | end 433 | 434 | def cmake_write_configuration_types(configuration_types, out) 435 | configuration_types_list = cmake_separate_arguments(configuration_types) 436 | puts_ind(out, "set(CMAKE_CONFIGURATION_TYPES \"#{configuration_types_list}\")" ) 437 | end 438 | 439 | def vc8_parse_file_list(project, vcproj_filter, files_str) 440 | file_group_name = vcproj_filter.attributes["Name"] 441 | if file_group_name.nil? 442 | file_group_name = "COMMON" 443 | end 444 | files_str[:name] = file_group_name 445 | puts_debug "parsing files group #{files_str[:name]}" 446 | 447 | vcproj_filter.elements.each("Filter") { |subfilter| 448 | # skip file filters that have a SourceControlFiles property 449 | # that's set to false, i.e. files which aren't under version 450 | # control (such as IDL generated files). 451 | # This experimental check might be a little rough after all... 452 | # yes, FIXME: on Win32, these files likely _should_ get listed 453 | # after all. We should probably do a platform check in such 454 | # cases, i.e. add support for a file_mappings.txt 455 | scfiles = subfilter.attributes["SourceControlFiles"] 456 | if not scfiles.nil? and scfiles.downcase == "false" 457 | puts "#{files_str[:name]}: SourceControlFiles set to false, listing generated files? --> skipping!" 458 | next 459 | end 460 | if files_str[:arr_sub_filters].nil? 461 | files_str[:arr_sub_filters] = Array.new() 462 | end 463 | subfiles_str = Files_str.new() 464 | files_str[:arr_sub_filters].push(subfiles_str) 465 | vc8_parse_file_list(project, subfilter, subfiles_str) 466 | } 467 | 468 | arr_sources = Array.new() 469 | vcproj_filter.elements.each("File") { |file| 470 | vc8_parse_file(project, file, arr_sources) 471 | } # |file| 472 | 473 | if not arr_sources.empty? 474 | files_str[:arr_files] = arr_sources 475 | end 476 | end 477 | 478 | def cmake_write_file_list(project, files_str, parent_source_group, arr_sub_sources_for_parent, out) 479 | group = files_str[:name] 480 | if not files_str[:arr_sub_filters].nil? 481 | arr_sub_filters = files_str[:arr_sub_filters] 482 | end 483 | if not files_str[:arr_files].nil? 484 | arr_local_sources = files_str[:arr_files].clone 485 | end 486 | 487 | # TODO: cmake is said to have a weird bug in case of parent_source_group being "Source Files" 488 | # http://www.mail-archive.com/cmake@cmake.org/msg05002.html 489 | if parent_source_group.nil? 490 | this_source_group = "" 491 | else 492 | if parent_source_group == "" 493 | this_source_group = group 494 | else 495 | this_source_group = "#{parent_source_group}\\\\#{group}" 496 | end 497 | end 498 | 499 | # process sub-filters, have their main source variable added to arr_my_sub_sources 500 | arr_my_sub_sources = Array.new() 501 | if not arr_sub_filters.nil? 502 | $myindent += 2 503 | arr_sub_filters.each { |subfilter| 504 | #puts "writing: #{subfilter}" 505 | cmake_write_file_list(project, subfilter, this_source_group, arr_my_sub_sources, out) 506 | } 507 | $myindent -= 2 508 | end 509 | 510 | group_tag = this_source_group.clone.gsub(/( |\\)/,'_') 511 | 512 | # process our hierarchy's own files 513 | if not arr_local_sources.nil? 514 | source_files_variable = "SOURCES_files_#{group_tag}" 515 | new_puts_ind(out, "set(#{source_files_variable}" ) 516 | arr_local_sources.each { |source| 517 | #puts "quotes now: #{source}" 518 | if source.include? ' ' 519 | # quote arguments with spaces (yes, I don't know how to 520 | # preserve quotes properly other than to actually open-code it 521 | # in such an ugly way - ARGH!!) 522 | puts_ind(out, " " + "\"#{source}\"") 523 | else 524 | puts_ind(out, " " + "#{source}") 525 | end 526 | } 527 | puts_ind(out, ")") 528 | # create source_group() of our local files 529 | if not parent_source_group.nil? 530 | puts_ind(out, "source_group(\"#{this_source_group}\" FILES ${#{source_files_variable}})") 531 | end 532 | end 533 | if not source_files_variable.nil? or not arr_my_sub_sources.empty? 534 | sources_variable = "SOURCES_#{group_tag}"; 535 | new_puts_ind(out, "set(SOURCES_#{group_tag}") 536 | $myindent += 2; 537 | # dump sub filters... 538 | arr_my_sub_sources.each { |source| 539 | puts_ind(out, "${#{source}}") 540 | } 541 | # ...then our own files 542 | if not source_files_variable.nil? 543 | puts_ind(out, "${#{source_files_variable}}") 544 | end 545 | $myindent -= 2; 546 | puts_ind(out, ")") 547 | # add our source list variable to parent return 548 | arr_sub_sources_for_parent.push(sources_variable) 549 | end 550 | end 551 | 552 | ################ 553 | # MAIN # 554 | ################ 555 | 556 | # write into temporary file, to avoid corrupting previous CMakeLists.txt due to disk space or failure issues 557 | tmpfile = Tempfile.new('vcproj2cmake') 558 | 559 | File.open(tmpfile.path, "w") { |out| 560 | 561 | out.puts "#" 562 | out.puts "# TEMPORARY Build file, AUTO-GENERATED by http://vcproj2cmake.sf.net" 563 | out.puts "# DO NOT CHECK INTO VERSION CONTROL OR APPLY \"PERMANENT\" MODIFICATIONS!!" 564 | out.puts "#" 565 | out.puts 566 | # Required version line to make cmake happy. 567 | out.puts "# >= 2.6 due to crucial set_property(... COMPILE_DEFINITIONS_* ...)" 568 | out.puts "cmake_minimum_required(VERSION 2.6)" 569 | 570 | out.puts "if(COMMAND cmake_policy)" 571 | # manual quoting of brackets in definitions doesn't seem to work otherwise, 572 | # in cmake 2.6.4-7.el5 with CMP0005 OLD. 573 | out.puts " if(POLICY CMP0005)" 574 | out.puts " cmake_policy(SET CMP0005 NEW) # automatic quoting of brackets" 575 | out.puts " endif(POLICY CMP0005)" 576 | out.puts 577 | out.puts " if(POLICY CMP0011)" 578 | out.puts " # we do want the includer to be affected by our updates," 579 | out.puts " # since it might define project-global settings." 580 | out.puts " cmake_policy(SET CMP0011 OLD)" 581 | out.puts " endif(POLICY CMP0011)" 582 | out.puts " if(POLICY CMP0015)" 583 | out.puts " # .vcproj contains relative paths to additional library directories, thus we need to be able to cope with that" 584 | out.puts " cmake_policy(SET CMP0015 NEW)" 585 | out.puts " endif(POLICY CMP0015)" 586 | out.puts "endif(COMMAND cmake_policy)" 587 | 588 | File.open(vcproj_filename) { |io| 589 | doc = REXML::Document.new io 590 | 591 | # try to point to cmake/Modules of the topmost directory of the vcproj2cmake conversion tree. 592 | # This also contains vcproj2cmake helper modules (these should - just like the CMakeLists.txt - 593 | # be within the project tree as well, since someone might want to copy the entire project tree 594 | # including .vcproj conversions to a different machine, thus all v2c components should be available) 595 | module_path_element = "\"#{$master_project_dir}/#{$v2c_module_path_local}\"" 596 | 597 | # NOTE: use set() instead of list(APPEND...) to prepend path 598 | # (otherwise not able to provide proper overrides) 599 | new_puts_ind(out, "set(CMAKE_MODULE_PATH #{module_path_element} ${CMAKE_MODULE_PATH})") 600 | 601 | # "export" our internal $v2c_config_dir_local variable (to be able to reference it in CMake scripts as well) 602 | new_puts_ind(out, "set(V2C_CONFIG_DIR_LOCAL \"#{$v2c_config_dir_local}\")") 603 | 604 | new_puts_ind(out, "# include the main file for pre-defined vcproj2cmake helper functions") 605 | puts_ind(out, "include(vcproj2cmake_func)") 606 | 607 | new_puts_ind(out, "# include the main file for pre-defined vcproj2cmake definitions") 608 | puts_ind(out, "include(vcproj2cmake_defs)") 609 | 610 | # this CMakeLists.txt-global optional include could be used e.g. 611 | # to skip the entire build of this file on certain platforms: 612 | # if(PLATFORM) message(STATUS "not supported") return() ... 613 | # (note that we appended CMAKE_MODULE_PATH _prior_ to this include()!) 614 | new_puts_ind(out, "include(${V2C_CONFIG_DIR_LOCAL}/hook_pre.txt OPTIONAL)") 615 | 616 | doc.elements.each("VisualStudioProject") { |project| 617 | 618 | project_name = project.attributes["Name"] 619 | 620 | $have_build_units = false 621 | 622 | configuration_types = Array.new() 623 | vc8_get_configuration_types(project, configuration_types) 624 | 625 | # we likely shouldn't declare this, since for single-configuration 626 | # generators CMAKE_CONFIGURATION_TYPES shouldn't be set 627 | ## configuration types need to be stated _before_ declaring the project()! 628 | #out.puts 629 | #cmake_write_configuration_types(configuration_types, out) 630 | 631 | # TODO: figure out language type (C CXX etc.) and add it to project() command 632 | new_puts_ind(out, "project( #{project_name} )") 633 | 634 | ## sub projects will inherit, and we _don't_ want that... 635 | # DISABLED: now to be done by MasterProjectDefaults_vcproj2cmake module if needed 636 | #puts_ind(out, "# reset project-local variables") 637 | #puts_ind(out, "set( V2C_LIBS )") 638 | #puts_ind(out, "set( V2C_SOURCES )") 639 | 640 | out.puts 641 | out.puts "# this part is for including a file which contains" 642 | out.puts "# _globally_ applicable settings for all sub projects of a master project" 643 | out.puts "# (compiler flags, path settings, platform stuff, ...)" 644 | out.puts "# e.g. have vcproj2cmake-specific MasterProjectDefaults_vcproj2cmake" 645 | out.puts "# which then _also_ includes a global MasterProjectDefaults module" 646 | out.puts "# for _all_ CMakeLists.txt. This needs to sit post-project()" 647 | out.puts "# since e.g. compiler info is dependent on a valid project." 648 | puts_ind(out, "# MasterProjectDefaults_vcproj2cmake is supposed to define") 649 | puts_ind(out, "# generic settings (such as V2C_HOOK_PROJECT, defined as e.g.") 650 | puts_ind(out, "# #{$v2c_config_dir_local}/hook_project.txt, and other hook include variables below).") 651 | puts_ind(out, "# NOTE: it usually should also reset variables V2C_LIBS, V2C_SOURCES etc.") 652 | puts_ind(out, "# as used below since they should contain directory-specific contents only, not accumulate!") 653 | # (side note: see "ldd -u -r" on Linux for superfluous link parts potentially caused by this!) 654 | puts_ind(out, "include(MasterProjectDefaults_vcproj2cmake OPTIONAL)") 655 | 656 | puts_ind(out, "# hook e.g. for invoking Find scripts as expected by") 657 | puts_ind(out, "# the _LIBRARIES / _INCLUDE_DIRS mappings created") 658 | puts_ind(out, "# by your include/dependency map files.") 659 | puts_ind(out, "include(${V2C_HOOK_PROJECT} OPTIONAL)") 660 | 661 | main_files = Files_str.new() 662 | project.elements.each("Files") { |files| 663 | vc8_parse_file_list(project, files, main_files) 664 | } 665 | arr_sub_sources = Array.new() 666 | $myindent += 2 667 | cmake_write_file_list(project, main_files, nil, arr_sub_sources, out) 668 | $myindent -= 2 669 | 670 | if not arr_sub_sources.empty? 671 | # add a ${V2C_SOURCES} variable to the list, to be able to append 672 | # all sorts of (auto-generated, ...) files to this list within 673 | # hook includes, _right before_ creating the target with its sources. 674 | arr_sub_sources.push("V2C_SOURCES") 675 | else 676 | puts "WARNING: #{project_name}: no source files at all!? (header-based project?)" 677 | end 678 | 679 | # AFAIK .vcproj implicitly adds the project root to standard include path 680 | # (for automatic stdafx.h resolution etc.), thus add this 681 | # (and make sure to add it with high priority, i.e. use BEFORE). 682 | new_puts_ind(out, "include_directories(BEFORE ${PROJECT_SOURCE_DIR})") 683 | 684 | new_puts_ind(out, "include(${V2C_HOOK_POST_SOURCES} OPTIONAL)") 685 | 686 | # ARGH, we have an issue with CMake not being fully up to speed with 687 | # multi-configuration generators (e.g. .vcproj): 688 | # it should be able to declare _all_ configuration-dependent settings 689 | # in a .vcproj file as configuration-dependent variables 690 | # (just like set_property(... COMPILE_DEFINITIONS_DEBUG ...)), 691 | # but with configuration-specific(!) include directories on .vcproj side, 692 | # there's currently only a _generic_ include_directories() command :-( 693 | # (dito with target_link_libraries() - or are we supposed to create an imported 694 | # target for each dependency, for more precise configuration-specific library names??) 695 | # Thus we should specifically specify include_directories() where we can 696 | # discern the configuration type (in single-configuration generators using 697 | # CMAKE_BUILD_TYPE) and - in the case of multi-config generators - pray 698 | # that the authoritative configuration has an AdditionalIncludeDirectories setting 699 | # that matches that of all other configs, since we're unable to specify 700 | # it in a configuration-specific way :( 701 | 702 | if config_multi_authoritative.empty? 703 | project_configuration_first = project.elements["Configurations/Configuration"].next_element 704 | if not project_configuration_first.nil? 705 | config_multi_authoritative = vc8_get_config_name(project_configuration_first) 706 | end 707 | end 708 | 709 | # target type (library, executable, ...) in .vcproj can be configured per-config 710 | # (or, in other words, different configs are capable of generating _different_ target _types_ 711 | # for the _same_ target), but in CMake this isn't possible since _one_ target name 712 | # maps to _one_ target type and we _need_ to restrict ourselves to using the project name 713 | # as the exact target name (we are unable to define separate PROJ_lib and PROJ_exe target names, 714 | # since other .vcproj file contents always link to our target via the main project name only!!). 715 | # Thus we need to declare the target variable _outside_ the scope of per-config handling :( 716 | target = nil 717 | 718 | project.elements.each("Configurations/Configuration") { |config| 719 | config_name = vc8_get_config_name(config) 720 | 721 | build_type_condition = "" 722 | if config_multi_authoritative == config_name 723 | build_type_condition = "CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE STREQUAL \"#{config_name}\"" 724 | else 725 | # YES, this condition is supposed to NOT trigger in case of a multi-configuration generator 726 | build_type_condition = "CMAKE_BUILD_TYPE STREQUAL \"#{config_name}\"" 727 | end 728 | 729 | new_puts_ind(out, "if(#{build_type_condition})") 730 | $myindent += 2 731 | 732 | # FIXME: do we need to actively reset CMAKE_MFC_FLAG / CMAKE_ATL_FLAG 733 | # (i.e. best also set it in case of 0?), since projects in subdirs shouldn't inherit? 734 | 735 | # 0 == no MFC 736 | # 1 == static MFC 737 | # 2 == shared MFC 738 | config_use_of_mfc = config.attributes["UseOfMFC"].to_i 739 | if config_use_of_mfc > 0 740 | new_puts_ind(out, "set(CMAKE_MFC_FLAG #{config_use_of_mfc})") 741 | end 742 | 743 | # ok, there's no CMAKE_ATL_FLAG yet, AFAIK, but still prepare 744 | # for it (also to let people probe on this in hook includes) 745 | config_use_of_atl = config.attributes["UseOfATL"].to_i 746 | if config_use_of_atl > 0 747 | # TODO: should also set the per-configuration-type variable variant 748 | new_puts_ind(out, "set(CMAKE_ATL_FLAG #{config_use_of_atl})") 749 | end 750 | 751 | arr_defines = Array.new() 752 | arr_flags = Array.new() 753 | config.elements.each('Tool[@Name="VCCLCompilerTool"]') { |compiler| 754 | 755 | arr_includes = Array.new() 756 | map_includes = Hash.new() 757 | if compiler.attributes["AdditionalIncludeDirectories"] 758 | include_dirs = compiler.attributes["AdditionalIncludeDirectories"].split(/[,;]/).sort.each { |s| 759 | incpath = normalize(s).strip 760 | #puts "include is '#{incpath}'" 761 | arr_includes.push(incpath) 762 | } 763 | # these mapping files may contain things such as mapping .vcproj "Vc7/atlmfc/src/mfc" into CMake ${MFC_INCLUDE} var 764 | read_mappings_combined(filename_map_inc, map_includes) 765 | end 766 | cmake_write_build_attributes("include_directories", "", out, arr_includes, map_includes, nil) 767 | 768 | if compiler.attributes["PreprocessorDefinitions"] 769 | 770 | compiler.attributes["PreprocessorDefinitions"].split(";").sort.each { |s| 771 | str_define, str_setting = s.strip.split(/=/) 772 | if str_setting.nil? 773 | arr_defines.push(str_define) 774 | else 775 | if str_setting =~ /[\(\)]+/ 776 | escape_char(str_setting, '\\(') 777 | escape_char(str_setting, '\\)') 778 | end 779 | arr_defines.push("#{str_define}=#{str_setting}") 780 | end 781 | } 782 | 783 | end 784 | 785 | if config_use_of_mfc == 2 786 | arr_defines.push("_AFXEXT", "_AFXDLL") 787 | end 788 | 789 | # TODO: add translation table for specific compiler flag settings such as MinimalRebuild: 790 | # simply make reverse use of existing translation table in CMake source. 791 | 792 | if compiler.attributes["AdditionalOptions"] 793 | arr_flags = compiler.attributes["AdditionalOptions"].split(";") 794 | end 795 | } 796 | 797 | config_type = config.attributes["ConfigurationType"].to_i 798 | 799 | # FIXME: hohumm, the position of this hook include is outdated, need to update it 800 | new_puts_ind(out, "# hook include after all definitions have been made") 801 | puts_ind(out, "# (but _before_ target is created using the source list!)") 802 | puts_ind(out, "include(${V2C_HOOK_POST_DEFINITIONS} OPTIONAL)") 803 | 804 | # create a target only in case we do have any meat at all 805 | #if not main_files[:arr_sub_filters].empty? or not main_files[:arr_files].empty? 806 | #if not arr_sub_sources.empty? 807 | if $have_build_units 808 | 809 | # first add source reference, then do linker setup, then create target 810 | 811 | new_puts_ind(out, "set(SOURCES") 812 | $myindent += 2 813 | arr_sub_sources.each { |group_tag| 814 | puts_ind(out, "${#{group_tag}}") 815 | } 816 | $myindent -= 2 817 | puts_ind(out, ")") 818 | 819 | # parse linker configuration... 820 | arr_dependencies = Array.new() 821 | arr_lib_dirs = Array.new() 822 | config.elements.each('Tool[@Name="VCLinkerTool"]') { |linker| 823 | deps = linker.attributes["AdditionalDependencies"] 824 | if deps and deps.length > 0 825 | deps.split.each { |lib| 826 | # FIXME possible to use lib = normalize(lib).strip here? 827 | lib = lib.gsub(/\\/, '/') 828 | arr_dependencies.push(File.basename(lib, ".lib")) 829 | } 830 | end 831 | 832 | lib_dirs = linker.attributes["AdditionalLibraryDirectories"] 833 | if lib_dirs and lib_dirs.length > 0 834 | lib_dirs.split(/[,;]/).each { |lib_dir| 835 | lib_dir = normalize(lib_dir).strip 836 | #puts "lib dir is '#{lib_dir}'" 837 | arr_lib_dirs.push(lib_dir) 838 | } 839 | end 840 | # TODO: support AdditionalOptions! (mention via 841 | # CMAKE_SHARED_LINKER_FLAGS / CMAKE_MODULE_LINKER_FLAGS / CMAKE_EXE_LINKER_FLAGS 842 | # depending on target type, and make sure to filter out options pre-defined by CMake platform 843 | # setup modules) 844 | } 845 | 846 | # write link_directories() (BEFORE establishing a target!) 847 | arr_lib_dirs.push("${V2C_LIB_DIRS}") 848 | 849 | map_lib_dirs = Hash.new() 850 | read_mappings_combined(filename_map_lib_dirs, map_lib_dirs) 851 | cmake_write_build_attributes("link_directories", "", out, arr_lib_dirs, map_lib_dirs, nil) 852 | 853 | # FIXME: should use a macro like rosbuild_add_executable(), 854 | # http://www.ros.org/wiki/rosbuild/CMakeLists , 855 | # https://kermit.cse.wustl.edu/project/robotics/browser/trunk/vendor/ros/core/rosbuild/rosbuild.cmake?rev=3 856 | # to be able to detect non-C++ file types within a source file list 857 | # and add a hook to handle them specially. 858 | 859 | # see VCProjectEngine ConfigurationTypes enumeration 860 | if config_type == 1 # typeApplication (.exe) 861 | target = project_name 862 | #puts_ind(out, "add_executable_vcproj2cmake( #{project_name} WIN32 ${SOURCES} )") 863 | # TODO: perhaps for real cross-platform binaries (i.e. 864 | # console apps not needing a WinMain()), we should detect 865 | # this and not use WIN32 in this case... 866 | new_puts_ind(out, "add_executable( #{target} WIN32 ${SOURCES} )") 867 | elsif config_type == 2 # typeDynamicLibrary (.dll) 868 | target = project_name 869 | #puts_ind(out, "add_library_vcproj2cmake( #{project_name} SHARED ${SOURCES} )") 870 | new_puts_ind(out, "add_library( #{target} SHARED ${SOURCES} )") 871 | elsif config_type == 4 # typeStaticLibrary 872 | target = project_name 873 | #puts_ind(out, "add_library_vcproj2cmake( #{project_name} STATIC ${SOURCES} )") 874 | new_puts_ind(out, "add_library( #{target} STATIC ${SOURCES} )") 875 | elsif config_type == 10 # typeGeneric (Makefile) [and possibly other things...] 876 | # TODO: we _should_ somehow support these project types... 877 | $stderr.puts "Project type #{config_type} not supported." 878 | exit 1 879 | elsif config_type == 0 # typeUnknown (utility) 880 | $stderr.puts "Project type #{config_type} not supported." 881 | exit 1 882 | end 883 | 884 | # write target_link_libraries() in case there's a target 885 | if not target.nil? 886 | arr_dependencies.push("${V2C_LIBS}") 887 | 888 | map_dependencies = Hash.new() 889 | read_mappings_combined(filename_map_dep, map_dependencies) 890 | cmake_write_build_attributes("target_link_libraries", "", out, arr_dependencies, map_dependencies, project_name) 891 | end # not target.nil? 892 | end # not arr_sub_sources.empty? 893 | 894 | new_puts_ind(out, "# e.g. to be used for tweaking target properties etc.") 895 | puts_ind(out, "include(${V2C_HOOK_POST_TARGET} OPTIONAL)") 896 | 897 | $myindent -= 2 898 | puts_ind(out, "endif(#{build_type_condition})") 899 | 900 | # NOTE: the commands below can stay in the general section (outside of 901 | # build_type_condition above), but only since they define 902 | # configuration-_specific_ settings only! 903 | if not target.nil? 904 | map_defines = Hash.new() 905 | read_mappings_combined(filename_map_def, map_defines) 906 | puts_ind(out, "if(TARGET #{target})") 907 | $myindent += 2 908 | cmake_set_target_property_compile_definitions(target, config_name, arr_defines, map_defines, out) 909 | cmake_set_target_property_compile_flags(target, config_name, arr_flags, out) 910 | $myindent -= 2 911 | puts_ind(out, "endif(TARGET #{target})") 912 | end 913 | } # [END per-config handling] 914 | 915 | # we can handle the following target stuff outside per-config handling (reason: see comment above) 916 | if not target.nil? 917 | # Make sure to keep CMake Name/Keyword (PROJECT_LABEL / VS_KEYWORD properties) in our converted file, too... 918 | # Hrmm, both project() _and_ PROJECT_LABEL reference the same project_name?? WEIRD. 919 | out.puts 920 | # no need to enclose this within "if(TARGET ...)" here since at this point 921 | # we really _should_ have a target available, 922 | # otherwise everything is broken anyway... 923 | cmake_set_target_property(target, "PROJECT_LABEL", project_name, out) 924 | project_keyword = project.attributes["Keyword"] 925 | if not project_keyword.nil? 926 | cmake_set_target_property(target, "VS_KEYWORD", project_keyword, out) 927 | end 928 | 929 | # keep source control integration in our conversion! 930 | # FIXME: does it really work? Then reply to 931 | # http://public.kitware.com/mantis/view.php?id=10237 !! 932 | if not project.attributes["SccProjectName"].nil? 933 | scc_project_name = project.attributes["SccProjectName"].clone 934 | # hmm, perhaps need to use CGI.escape since chars other than just '"' might need to be escaped? 935 | # NOTE: needed to clone() this string above since otherwise modifying (same) source object!! 936 | # We used to escape_char('"') below, but this was problematic 937 | # on VS7 .vcproj generator since that one is BUGGY (GIT trunk 938 | # 201007xx): it should escape quotes into XMLed """ yet 939 | # it doesn't. Thus it's us who has to do that and pray that it 940 | # won't fail on us... (but this bogus escaping within 941 | # CMakeLists.txt space might lead to severe trouble 942 | # with _other_ IDE generators which cannot deal with a raw """). 943 | # Note that perhaps we should also escape all other chars 944 | # as in CMake's EscapeForXML() method. 945 | scc_project_name.gsub!(/"/, """) 946 | # hrmm, turns out having SccProjectName is no guarantee that both SccLocalPath and SccProvider 947 | # exist, too... (one project had SccProvider missing) 948 | if not project.attributes["SccLocalPath"].nil? 949 | scc_local_path = project.attributes["SccLocalPath"].clone 950 | end 951 | if not project.attributes["SccProvider"].nil? 952 | scc_provider = project.attributes["SccProvider"].clone 953 | end 954 | out.puts 955 | cmake_set_target_property(target, "VS_SCC_PROJECTNAME", scc_project_name, out) 956 | if scc_local_path 957 | escape_backslash(scc_local_path) 958 | escape_char(scc_local_path, '"') 959 | cmake_set_target_property(target, "VS_SCC_LOCALPATH", scc_local_path, out) 960 | end 961 | if scc_provider 962 | escape_char(scc_provider, '"') 963 | cmake_set_target_property(target, "VS_SCC_PROVIDER", scc_provider, out) 964 | end 965 | end 966 | # TODO: perhaps there are useful Xcode (XCODE_ATTRIBUTE_*) properties to convert? 967 | end # not target.nil? 968 | new_puts_ind(out, "v2c_rebuild_on_update(#{project_name} \"${CMAKE_CURRENT_SOURCE_DIR}/#{p_vcproj.basename}\" ${CMAKE_CURRENT_LIST_FILE} \"#{script_name}\" \"#{$master_project_dir}\")") 969 | } 970 | new_puts_ind(out, "include(${V2C_HOOK_POST} OPTIONAL)") 971 | } 972 | # Close file, since Fileutils.mv on an open file will barf on XP 973 | out.close 974 | } 975 | 976 | # make sure to close that one as well... 977 | tmpfile.close 978 | 979 | # Since we're forced to fumble our source tree (a definite no-no in all other cases!) 980 | # by writing our CMakeLists.txt there, use a write-back-when-updated approach 981 | # to make sure we only write back the live CMakeLists.txt in case anything did change. 982 | # This is especially important in case of multiple concurrent builds on a shared 983 | # source on NFS mount. 984 | 985 | configuration_changed = false 986 | have_old_file = false 987 | if File.exists?(output_file) 988 | have_old_file = true 989 | # hmm, another possibility would be File.cmp() (ftools) 990 | if not FileUtils.cmp(tmpfile.path, output_file) 991 | configuration_changed = true 992 | end 993 | else 994 | configuration_changed = true 995 | end 996 | 997 | if configuration_changed 998 | if have_old_file 999 | # move away old file 1000 | mv(output_file, output_file + ".previous") 1001 | end 1002 | # activate our version 1003 | mv(tmpfile.path, output_file) 1004 | 1005 | puts "Wrote #{output_file}" 1006 | puts "Finished. You should make sure to have all important v2c settings includes such as vcproj2cmake_defs.cmake somewhere in your CMAKE_MODULE_PATH" 1007 | else 1008 | puts "No settings changed, #{output_file} not updated." 1009 | # tmpfile will auto-delete when finalized... 1010 | 1011 | # Some make dependency mechanisms might require touching (timestamping) the unchanged(!) file 1012 | # to indicate that it's up-to-date, 1013 | # however we won't do this here since it's not such a good idea. 1014 | # Any user who needs that should do a manual touch subsequently. 1015 | end 1016 | --------------------------------------------------------------------------------