Skip to content

Instantly share code, notes, and snippets.

@Fat-Zer
Last active March 29, 2020 02:51
Show Gist options
  • Save Fat-Zer/4035188 to your computer and use it in GitHub Desktop.
Save Fat-Zer/4035188 to your computer and use it in GitHub Desktop.
autotools-cmake-convert.pl
#!/usr/bin/env perl
# Usage sample:
# find <dir> -iname Makefile.am | while read m; do
# echo "=== processing $m ===";
# ../scripts/autotools-cmake-convert.pl ${m} >"$(dirname $m)/CMakeLists.txt";
# done
use v5.10.1;
use experimental qw(smartmatch);
use File::Basename;
use File::Find::Rule;
######### settings ##############################
$STR_LENGTH = 79;
$BOX_COMMENT_START = 5;
# $BOX_LENGTH = $STR_LENGTH;
$BOX_LENGTH = 49;
$TAB_SYMBOL = ' ';
$TAB_WIDTH = 2;
$PRINT_INFO='no';
$PRINT_AUTO_HEADER='yes';
$PRINT_COPYRIGHT='no';
######### utils #################################
sub usage () {
print STDERR "Usage:\n";
print STDERR " $0 Makefile.am\n";
}
sub error ($) {
print STDERR "[\033[1;31m !!! \033[0m] $_[0]\n";
}
sub warning ($) {
print STDERR "[\033[1;33m ??? \033[0m] $_[0]\n";
}
sub info ($) {
if( $PRINT_INFO eq 'yes' ) {
print STDERR "[\033[1;32m +++ \033[0m] $_[0]\n";
}
}
sub box_comment ($@) {
my $rv, $str;
if( scalar(@_) == 0 ) {
$rv = "#" x $BOX_LENGTH;
} else {
$rv = "#" x $BOX_COMMENT_START;
foreach $str ( @_ ) {
$rv .= ' '.$str;
}
$rv .= ' #';
$rv .= '#' x ($BOX_LENGTH - length($rv));
}
$rv;
}
sub tab_string ($) {
my $tab_num=$_[0];
$TAB_SYMBOL x ($TAB_WIDTH * $tab_num);
}
sub fix_tabs ($) {
my $tab_num=$_[0];
my $str = $_[1];
$str =~ s/^\s*//;
&tab_string($tab_num).$str;
}
sub fix_width ($$) {
my $tab_num = $_[0];
my $str = $_[1];
my $rv = "";
$str=&fix_tabs($tab_num, $str);
while( length($str)> $STR_LENGTH) {
my $split_pos, $tmp;
$split_pos=rindex( $str, " ", $STR_LENGTH);
$tmp = substr($str, 0, $split_pos)."\n";
if( $tmp =~ /^\s*$/) {
last;
}
$rv.=$tmp;
$str = substr($str, $split_pos);
$str=&fix_tabs($tab_num+1, $str);
}
if( ! $str =~ /^\s*$/) {
$rv .= $str."\n";
} else {
$rv.="\n";
}
$rv;
}
sub print_auto_header() {
print "# This file is genereted by trinity-automake-cmake-convert script by Fat-Zer\n" ;
}
sub print_copyright () {
print <<EOF
#################################################
#
# (C) 2016 Alexander Golubev
# fatzer2 (AT) gmail.com
#
# Improvements and feedback are welcome
#
# This file is released under GPL >= 2
#
#################################################
EOF
;
}
sub read_makefile_hash (*) {
my $f=$_[0];
my %rv;
my $k;
seek($f, 0, 0);
while(<$f>){
if( /^[^#]\S+\s*=\s*/ ) {
$k = $_;
$k =~ s/\s?=.*//;
$k =~ s/^\s+//;
$k =~ s/\s+$//;
s/\S+\s*=\s*//;
while( /\\\n$/ ) {
my $next = <$f>;
$next =~ s/^\s*//;
s/\s*\\\n$/ /;
$_ .= $next;
}
s/\s/ /g;
s/ +/ /g;
s/ $//;
s/^ //;
if( exists $rv{ $k }) {
&error("Second declaration of statement \"$k\"");
} else {
$rv{ $k } = $_;
}
}
}
%rv;
}
sub do_skip_line () {
if( $skip_line != 0 ) {for($skip_line; $skip_line>0; $skip_line--) { print "\n"; } }
}
######### read Makefile.in ######################
if ( $#ARGV != 1 - 1 ) {
&usage;
die("Bad arguments\n");
};
open(INP_F, "< $ARGV[0]") || die "cannot open $ARGV[0]";
%makefile_hash=&read_makefile_hash(INP_F);
close INP_F;
######### determin the pkg_dir name #############
$full_pkg_dir = dirname($ARGV[0]);
$pkg_dir = basename($full_pkg_dir);
######### detect what statements we got #########
@compilation_statements=(
"bin_PROGRAMS",
"lib_LTLIBRARIES",
"tdeinit_LTLIBRARIES",
"kde_module_LTLIBRARIES",
"noinst_LTLIBRARIES",
"check_PROGRAMS");
@data_statements=(
"xdg_apps_DATA",
"kde_kcfg_DATA",
".*_DATA");
@headers_statements=(
".*_HEADERS",
);
@headers_statements_exclude=(
"noinst_HEADERS",
);
@doc_statements=(
"KDE_MANS",
"KDE_DOCS", );
@translation_statements=(
"POFILES", );
@subdir_statements=(
"SUBDIRS");
@icon_statements=(
"KDE_ICON");
$have_compilation=0;
$have_data=0;
$have_subdir=0;
$have_icon=0;
$have_doc=0;
$have_translation=0;
foreach $type ("subdir", "compilation", "icon", "data", "headers", "doc", "translation") {
foreach $var (@{${type}."_statements"}) {
my @vals = grep ( /${var}/ , keys %makefile_hash);
if( @vals && (
not @{${type}."_statements_exclude"} || map ( grep ( !/$_/, @vals), @{${type}."_statements_exclude"} ) ) ) {
${"have_".$type}=1;
&info("Found $type statment(s)");
last;
}
}
}
######### test if we fully support all statements #
@supported_statements=(
@compilation_statements,
@data_statements,
@subdir_statements,
@icon_statements,
@doc_statements,
@translation_statements,
@data_statements,
@headers_statements,
"INCLUDES",
"noinst_HEADERS",
"METASOURCES",
"EXTRA_DIST",
"AM_LDFLAGS",
"AM_CPPFLAGS",
"LDADD",
"KDE_LANG",
"TOPLEVEL_LANG",
"AUTOMAKE_OPTIONS");
@checkme_statements=(
"check_PROGRAMS",
"KDE_MANS",
"TOPLEVEL_LANG",
"AUTOMAKE_OPTIONS");
@supported_la=(
"SOURCES",
"LIBADD",
"LDFLAGS",
"LDADD",
"METASOURCES");
foreach $key (keys %makefile_hash) {
if( !( (grep(/^$key$/, @supported_statements)) ||
(grep {$key =~ /_(la_)?$_$/} @supported_la) ||
($key =~ /^.*(_DATA|_HEADERS|dir)$/ ) ) ) {
&error("Statement $key is not supported by the script");
} elsif ( grep(/^$key$/, @checkme_statements) ) {
given ($key) {
when("check_PROGRAMS")
{ &error("check_PROGRAMS statement is not supported in cmake."); }
when("KDE_MANS")
{ &error("KDE_MANS found but KDE_DOCS not.")
if ( !exists $makefile_hash{"KDE_DOCS"} ) ; }
default {
&warning("Statement $key should be checked manually"); }
}
}
}
######### header ################################
if( $PRINT_COPYRIGHT eq 'yes' ) {
&print_copyright;
$skip_line=1;
}
if( $PRINT_AUTO_HEADER eq 'yes' ) {
&print_auto_header($ARGV[0]);
$skip_line=1;
}
######### subdirs ###############################
if( $have_subdir ) {
# test if we realy got subdirs
if( File::Find::Rule->directory
->maxdepth(1)
->not(File::Find::Rule->new->name(qr/^\.\.?$/))
->in($full_pkg_dir) == 0 ) {
$have_subdir=0;
&warning("Seems we haven't got subdirs; skipping it;");
} else {
my $was_printed=0;
foreach $subdir ( split(/\s/, $makefile_hash{"SUBDIRS"}) ) {
if( $subdir =~ /\Q$(AUTODIRS)\E/ ){
&info("Adding auto subdirectory target");
do_skip_line;
print "tde_auto_add_subdirectories( )\n";
$was_printed=1;
}elsif( $subdir =~ /^\w+$/ ) {
&info("Adding subdirectory \"$subdir\"");
do_skip_line;
print "add_subdirectory( $subdir )\n";
$was_printed=1;
} else {
&error("Subdir $subdir seems to be strange. Check it!");
}
}
if( $was_printed=1 ) {
$skip_line += 1;
}
}
}
######### includes links etc ####################
if( $have_compilation ) {
if( exists $makefile_hash{"INCLUDES"}) {
foreach $inc ( split( /\s+/, $makefile_hash{"INCLUDES"}) ) {
if( $inc eq "\$\(all_includes\)" ) {
&info ("Found general include statement");
} elsif( $inc =~ /^\Q-I$(top_srcdir)\E\/(.*)$/ ) {
$t = "\${CMAKE_SOURCE_DIR}/" . $1;
&info("Found additional include: $t");
push (@additional_includes, "$t");
} elsif( $inc =~ /^\Q-I$(srcdir)\E\/(.*)$/ ) {
$path = $1;
if ($path =~ /\Q..\E/) {
&warning("INCLUDES have relative path: $inc.");
} else {
$t = "\${CMAKE_CURRENT_SOURCE_DIR}/" . $path;
&info("Found additional include: $t");
push (@additional_includes, "$t");
}
} else {
&warning("INCLUDES statement has unsuported value: $inc.");
}
}
}
do_skip_line;
&info("Adding include dirs");
$entry = "include_directories(\n";
$entry .= " \${CMAKE_BINARY_DIR}\n";
$entry .= " \${CMAKE_CURRENT_BINARY_DIR}\n";
$entry .= " \${CMAKE_CURRENT_SOURCE_DIR}\n";
$entry .= " " . join( "\n ", @additional_includes ) . "\n" if @additional_includes;
$entry .= " \${TDE_INCLUDE_DIR}\n";
$entry .= " \${TQT_INCLUDE_DIRS}\n";
$entry .= ")\n";
print $entry;
&info("Adding default link dirs");
print <<EOF
link_directories(
\${TQT_LIBRARY_DIRS}
)
EOF
;
$skip_line = 1;
######### definitions ###########################
if( exists $makefile_hash{"AM_CPPFLAGS"}) {
my $defs=$makefile_hash{"AM_CPPFLAGS"};
my $entry;
$entry = "add_definitions(\n";
foreach $def (split(/\s/, $defs) ) {
if ($def =~ /^-D/ ) {
$entry .= fix_width( 1, "$def");
&info("Adding definition \"$def\"");
} else {
&warning("Definition \"$def\" wasn't added; definitions may be incomplete");
}
}
$entry .= ")\n";
do_skip_line;
print $entry;
$skip_line = 1;
}
######### binaries ##############################
%bin_types=(
"bin_PROGRAMS", "executable",
"check_PROGRAMS", "test",
"tdeinit_LTLIBRARIES", "tdeinit",
"kde_module_LTLIBRARIES" ,"kpart",
"noinst_LTLIBRARIES", "static",
"lib_LTLIBRARIES", "shared");
foreach $type (keys %bin_types ) {
foreach $target ( split(/\s/, $makefile_hash{$type}) ) {
my ( $prefix, $name, $sources, $ldflags, $link, $entry, $ver);
$prefix = $target;
$prefix =~ s/\./_/g;
$name = $target;
$name =~ s/\.la$//;
given($bin_types{$type}) {
when('static') { $name =~ s/^lib//; }
when('shared') { $name =~ s/^lib//; }
}
$sources = $makefile_hash{$prefix."_SOURCES"};
if( $sources =~ /^\s*$/) { &error("SOURCES for $name are empty"); }
$ldflags = $makefile_hash{$prefix."_LDFLAGS"};
$ldflags .= " ".$makefile_hash{$prefix."_LDADD"};
$ldflags .= " ".$makefile_hash{$prefix."_LIBADD"};
$ldflags .= " ".$makefile_hash{"LIBADD"};
$ldflags .= " ".$makefile_hash{"LDADD"};
$ldflags .= " ".$makefile_hash{"AM_LDFLAGS"};
$ver=$ldflags;
if( $ver =~ /-version-info (\d(\d|\.|:)+)/ ) {
$ver =~ s/^.*-version-info (\d(\d|\.|:)+).*$/\1/;
$ver =~ s/:/./g;
} else {
$ver="";
}
$link = "";
%autotools_cmake_link_map = (
"\$(LIB_TDEPRINT)" , "tdeprint-shared" ,
"\$(LIB_TDEHTML)" , "tdehtml-shared" ,
"\$(LIB_TDEIO)" , "tdeio-shared" ,
"\$(LIB_TDEUI)" , "tdeui-shared" ,
"\$(LIB_TDECORE)" , "tdecore-shared" ,
"\$(LIB_TDEDNSSD)" , "tdednssd-shared" ,
"\$(LIB_TDESYCOCA)", "tdeio-shared" ,
"\$(LIB_TDEGAMES)" , "tdegames-shared" ,
"-lkateinterfaces" , "kateinterfaces-shared",
"-lkonq" , "konq-shared" );
foreach $ld ( split ( /\s+/, $ldflags ) ) {
if ( exists $autotools_cmake_link_map{$ld} ) {
$link .= " " . $autotools_cmake_link_map{$ld};
} elsif ( $ld =~ /\/lib([^\/]+?)\.la/ ) {
$link .= " $1-shared";
}
}
$link =~ s/^\s+//
# TODO: check if AUTOMOC is mandatory
&info("Adding $bin_types{$type} target $name");
$entry = "\n".&box_comment( $name." ($bin_types{$type})")."\n\n";
given($bin_types{$type}) {
when('executable')
{ $entry .= "tde_add_executable( $name AUTOMOC\n"; }
when('test')
{ $entry .= "tde_add_executable( $name AUTOMOC\n"; }
when('tdeinit')
{ $entry .= "tde_add_tdeinit_executable( $name AUTOMOC\n"; }
when('kpart')
{ $entry .= "tde_add_kpart( $name AUTOMOC\n"; }
when('static')
{ $entry .= "tde_add_library( $name STATIC_PIC AUTOMOC\n"; }
when('shared')
{ $entry .= "tde_add_library( $name SHARED AUTOMOC\n"; }
}
$entry .= fix_width( 1, "SOURCES $sources");
$entry .= fix_width( 1, "VERSION $ver") if( $ver );
$entry .= fix_width( 1, "LINK $link") if( $link );
given($bin_types{$type}) {
when('executable')
{ $entry .= fix_width( 1, "DESTINATION \$\{BIN_INSTALL_DIR\}"); }
when('shared')
{ $entry .= fix_width( 1, "DESTINATION \$\{LIB_INSTALL_DIR\}"); }
when('kpart')
{ $entry .= fix_width( 1, "DESTINATION \$\{PLUGIN_INSTALL_DIR\}"); }
when('test') {}
when('tdeinit') {}
when('static') {}
}
$entry .= ")\n";
do_skip_line;
print $entry;
$skip_line = 1;
if( $bin_types{$type} eq 'test' ) {
$entry = "add_test( NAME $name-test\n";
$entry .= fix_width( 1, "COMMAND $name)");
do_skip_line;
print $entry;
$skip_line = 1;
}
}
}
$skip_line+=1;
}
######### icons #################################
if( $have_icon ) {
if( $have_compilation || $have_subdir) {
do_skip_line;
print &box_comment("icons")."\n";
$skip_line = 1;
}
if( exists $makefile_hash{"KDE_ICON"}) {
my ( $entry, $icons );
$icons = $makefile_hash{"KDE_ICON"};
if ( $icons =~ /^AUTO$/ ) {
&info("Adding auto icon enrty");
$entry = &fix_width( 0, "tde_install_icons( )");
} else {
&info("Adding instalation of icon \"$icons\" to default dirs");
$entry = &fix_width( 0, "tde_install_icons( ".$icons." )");
}
do_skip_line;
print $entry."\n";
$skip_line = 1;
}
}
######### other data ############################
sub process_install_data (*) {
$data_designator= shift;
%data_dirs=(
'xdg_apps', '${XDG_APPS_INSTALL_DIR}',
'kde_kcfg', '${KCFG_INSTALL_DIR}',
'autostart', '${AUTOSTART_INSTALL_DIR}');
%autotools_cmake_dir_map= (
'$(kde_datadir)', '${DATA_INSTALL_DIR}',
'$(kde_appsdir)', '${APPS_INSTALL_DIR}',
'$(kde_mimedir)', '${MIME_INSTALL_DIR}',
'$(kde_servicesdir)', '${SERVICES_INSTALL_DIR}',
'$(kde_confdir)', '${CONFIG_INSTALL_DIR}',
'$(includedir)', '${INCLUDE_INSTALL_DIR}',
'$(kde_datadir)/kconf_update', '${KCONF_UPDATE_INSTALL_DIR}',
'$(datadir)/autostart', '${AUTOSTART_INSTALL_DIR}',
'$(kde_servicetypesdir)', '${SERVICETYPES_INSTALL_DIR}');
foreach $data (grep( /.*$data_designator/ , keys %makefile_hash)) {
my $type = $data;
$type =~ s/$data_designator$//;
if( exists $makefile_hash{$type."dir"} ) {
my $ok=0;
$dir=$makefile_hash{$type."dir"};
foreach $guess (keys %autotools_cmake_dir_map ) {
if( $dir =~ /\Q$guess\E/ ) {
$dir =~ s/\Q$guess\E/$autotools_cmake_dir_map{$guess}/;
$ok=1;
}
}
if( $ok ) {
$data_dirs{$type} = $dir;
} else {
&error("Failed to guess dir for ${data}");
}
} else {
if( ! grep( $type , keys %data_dirs) ) {
&error("Destination for ${data} is missing");
}
}
}
foreach $type ( keys %data_dirs ) {
if( exists $makefile_hash{$type."$data_designator"}) {
my ( $entry, $files, $dest );
$files = $makefile_hash{$type."$data_designator"};
$dest = $data_dirs{$type};
&info("Adding instalation of files \"$files\" to $dest");
$entry = &fix_width( 0, "install( FILES ".$files);
$entry .= &fix_width(1, "DESTINATION $dest");
$entry .= ")\n";
do_skip_line;
print $entry;
$skip_line = 1;
}
}
}
if( $have_data ) {
if( $have_compilation || $have_subdir || $have_icon) {
do_skip_line;
print &box_comment("other data")."\n";
$skip_line = 1;
}
&process_install_data( "_DATA" );
$skip_line += 1;
}
if( $have_headers ) {
if( $have_compilation || $have_subdir || $have_icon || $have_data ) {
do_skip_line;
print &box_comment("headers")."\n";
$skip_line = 1;
}
&process_install_data( "_HEADERS" );
$skip_line += 1;
}
######### doc ###################################
if( $have_doc ) {
if( File::Find::Rule
->maxdepth(1)
->name("*.docbook")
->in($full_pkg_dir) == 0 ) {
$have_doc=0;
&warning("Seems we haven't got docs; skipping it;");
} else {
if( $have_compilation || $have_subdir || $have_icon || $have_data) {
do_skip_line;
print &box_comment("documentation")."\n";
$skip_line = 1;
}
if($makefile_hash{'KDE_DOCS'} =~ /\s/ ) {
&error("KDE_DOCS got multiple values. it is not supported yet.");
} else {
my $dest;
my $lang = $makefile_hash{'KDE_LANG'};
my $doc=$makefile_hash{'KDE_DOCS'};
# add NOINDEX if it doesn't exists
my $index_str="";
if( File::Find::Rule
->maxdepth(1)
->name("index.docbook")
->in($full_pkg_dir) == 0 ) {
$index_str="\nNOINDEX";
}
if ($doc =~ /^AUTO$/ ) {
if( $pkg_dir !~ /^(\w|-)+$/ ) {
&error("using bad pkg_dir name for docs.");
}
&info("adding handbook target with destination: \"$pkg_dir\" for LANG=$lang.");
$dest=$pkg_dir;
} else {
&info("adding handbook target with $doc destination for LANG=$lang.");
$dest=$doc;
}
if( $lang !~ /^en$/ ) {
if( $lang !~ /^\w{2,3}(_\w\w|@\w+)?$/ ) {
&error("using bad LANG=\"$lang\" name for docs.");
}
do_skip_line;
print "tde_create_handbook(\n";
print &fix_width( 1,"DESTINATION $dest");
print &fix_width( 1, "$index_str") if( $index_str );
print &fix_width( 1, "LANG $lang").")\n";
$skip_line += 1;
} else {
print &fix_width( 0, "tde_create_handbook( DESTINATION $dest $index_str )")."\n";
}
}
}
$skip_line += 1;
}
######### translation ###########################
if( $have_translation ) {
if( File::Find::Rule
->maxdepth(1)
->name("*.po")
->in($full_pkg_dir) == 0 ) {
$have_translation=0;
&warning("Seems we haven't got translations; skipping it;");
} else {
if( $have_compilation || $have_subdir || $have_icon || $have_data || $have_doc) {
do_skip_line;
print &box_comment("translations")."\n";
$skip_line += 1;
}
my $lang = $makefile_hash{'KDE_LANG'};
my $po=$makefile_hash{'POFILES'};
if ($po !~ /^AUTO$/ ) {
&error("POFILES != AUTO is not supported by the script.");
} else {
if( $lang !~ /^\w{2,3}(_\w\w|@\w+)?$/ ) {
&error("using bad LANG=\"$lang\" name for translation.");
}
&info( "adding translation LANG=\"$lang\"." );
do_skip_line;
print &fix_width( 0,"tde_create_translation( LANG $lang )");
}
}
$skip_line += 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment