package XS::Install::CMake; use strict; use warnings; use Env qw/@PATH/; use Cwd 'abs_path'; sub configure { my $result = run(@_); return import_cmake_properites($result); } sub run { my ($bdir, $props_dir, $target, $options) = @_; $props_dir = abs_path($props_dir); # in case, it has symlink in path: cmake cannot work with build dirs with symlink parts my $ok = eval { require Alien::cmake3; 1 }; die "This module requires Alien::cmake3 to build.\n" unless $ok; unshift @PATH, Alien::cmake3->bin_dir; open(CMAKELISTS, '>', "$props_dir/CMakeLists.txt") or die $!; print CMAKELISTS <<'EOS'; cmake_minimum_required(VERSION 3.5) project(GetProps) macro(find_package) set(find_package_ARGS ${ARGV}) list(REMOVE_ITEM find_package_ARGS "REQUIRED") list(APPEND find_package_ARGS "QUIET") _find_package(${find_package_ARGS}) endmacro() if (WIN32) list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .dll) endif() function(is_good_lib_item ret name) set(${ret} FALSE PARENT_SCOPE) if(${name} MATCHES "debug|optimized|general|PRIVATE|PUBLIC|INTERFACE" OR ${name} MATCHES "^-.*" OR TARGET ${name}) set(${ret} TRUE PARENT_SCOPE) endif() find_library(${name}_is_good ${name}) if(${name}_is_good) set(${ret} TRUE PARENT_SCOPE) endif() endfunction() macro(target_link_libraries) set(TARGET_LINK_LIBS_ARGS ${ARGV}) list(REMOVE_AT TARGET_LINK_LIBS_ARGS 0) foreach(ARG ${TARGET_LINK_LIBS_ARGS}) is_good_lib_item(is_good ${ARG}) message(STATUS "checking " ${ARG} " -> " ${is_good}) if(${is_good}) list(APPEND TARGET_LINK_LIBS_OK ${ARG}) endif() endforeach() if (TARGET_LINK_LIBS_OK) message(STATUS "linking:" ${ARGV0} ${TARGET_LINK_LIBS_OK}) _target_link_libraries(${ARGV0} ${TARGET_LINK_LIBS_OK}) endif() endmacro() macro(add_library) set(args ${ARGV}) list(FIND args IMPORTED index) if (index GREATER_EQUAL 0) MATH(EXPR index "${index}+1") list (INSERT args ${index} GLOBAL) endif() message(STATUS "add_library: ${args}") _add_library(${args}) endmacro() add_subdirectory(".." "../build") function(get_all_libs_locations ret tgt) set(self_loc "") get_target_property(lret ${tgt} TYPE) if (NOT ${lret} STREQUAL INTERFACE_LIBRARY) get_target_property(lret ${tgt} IMPORTED_LOCATION) message(STATUS "got:" ${lret} "for" ${tgt}) if (lret) set(self_loc ${lret}) endif() endif() get_target_property(deps ${tgt} INTERFACE_LINK_LIBRARIES) message(STATUS "deps:" ${deps} "for" ${tgt}) foreach(lib ${deps}) if (TARGET ${lib}) get_all_libs_locations(lret ${lib}) list(APPEND self_loc ${lret}) endif() endforeach() set(${ret} ${self_loc} PARENT_SCOPE) endfunction() function(write_all_libs_inc tgt) get_target_property(deps ${tgt} INTERFACE_LINK_LIBRARIES) message(STATUS "deps inc:" ${deps} "for" ${tgt}) foreach(lib ${deps}) if (TARGET ${lib}) get_target_property(lret ${lib} INTERFACE_INCLUDE_DIRECTORIES) message(STATUS "GOT_INC=" ${lret}) write_all_libs_inc(${lib}) endif() endforeach() endfunction() foreach(PROP ${REQUESTED_PROPS}) get_target_property(GOT_RES ${REQUESTED_TARGET} ${PROP}) if (GOT_RES) message(STATUS "GOT_${PROP}=" "${GOT_RES}") else (GOT_RES) message(STATUS "GOT_${PROP}=") endif (GOT_RES) endforeach() get_target_property(libs ${REQUESTED_TARGET} INTERFACE_LINK_LIBRARIES) if(libs) foreach(lib ${libs}) if (NOT TARGET ${lib}) list(APPEND lib_result ${lib}) endif() endforeach() message(STATUS "GOT_FILTERED_LINK_LIBRARIES=" "${lib_result}") endif() foreach(TARG ${CONF_TARGETS}) separate_arguments(OPT UNIX_COMMAND ${${TARG}_COMP_OPTIONS}) target_compile_options(${TARG} PRIVATE ${OPT}) endforeach() get_all_libs_locations(locs ${REQUESTED_TARGET}) message(STATUS "GOT_LOCATIONS=" "${locs}") write_all_libs_inc(${REQUESTED_TARGET}) EOS close(CMAKELISTS); my @properties = qw(INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_LINK_DIRECTORIES INTERFACE_LINK_LIBRARIES INTERFACE_LINK_OPTIONS OUTPUT_NAME NAME IMPORT_PREFIX IMPORT_SUFFIX LINK_LIBRARIES LINK_OPTIONS LINK_FLAGS IMPORTED_LOCATION); my $prop_list = join ';', @properties; return `cd $props_dir && cmake . -G "Unix Makefiles" $options -DREQUESTED_TARGET=$target -DREQUESTED_PROPS="$prop_list"`; } sub import_cmake_properites { my $source = shift; my $result = { INSTALL_INCLUDE => _get_prop($source, 'INTERFACE_INCLUDE_DIRECTORIES', 'INSTALL_INTERFACE'), BUILD_INCLUDE => _get_prop($source, 'INTERFACE_INCLUDE_DIRECTORIES', 'BUILD_INTERFACE'), INC => _get_prop($source, 'INC', 'INSTALL_INTERFACE'), }; my $libs = _get_prop($source, 'FILTERED_LINK_LIBRARIES'); my $link_opts = _get_prop($source, 'INTERFACE_LINK_OPTIONS'); my $locations = _get_prop($source, 'LOCATIONS'); push ( @$link_opts, map({($_ =~ /^-/) ? $_ : "-l$_"} @$libs), @$locations ); $result->{"LIBS"} = $link_opts; $result->{"DEFINE"} = [map {"-D$_"} @{_get_prop($source, 'INTERFACE_COMPILE_DEFINITIONS')}]; $result->{"CCFLAGS"} = _get_prop($source, 'IMPORT_COMPILE_OPTIONS'); my $ok = eval { require Alien::cmake3; 1 }; die "This module requires Alien::cmake3 to build.\n" unless $ok; $result->{"CMAKE_BIN"} = File::Spec->catfile(Alien::cmake3->bin_dir, Alien::cmake3->exe); return $result; } sub _get_raw_prop { my ($source, $prop_name) = @_; my @result = ($source =~ /(?<=GOT_$prop_name=)(.+?)$/gm); return \@result; } sub _split_cmake_generator { my $str = shift; return ; } sub _transform_cmake_generator { my ($vals, $generator_key) = @_; my @result; for my $str (@$vals) { my @splited = ($str =~ /(?:\$<.*?>)|(?:[^\$;]+)/g); for my $val (@splited) { if ($val =~ /\$<$generator_key:(.*)>/g) { push @result, split(/;/, $1); next; } if ($val =~ /\$/g) { #other generate expression next; } push @result, $val; #pure value without generators } } return \@result; } sub _get_prop { my ($source, $prop_name, $generator_key) = @_; my $res = _get_raw_prop($source, $prop_name); if ($generator_key) { return _transform_cmake_generator($res,$generator_key); } else { return [map{split(";", $_)} @$res]; } } 1;