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;