#!/usr/bin/perl -w
# NAME: wlog2cmake.pl
# AIM: Try to convert a win nmake log to cmake
# 13/06/2015 geoff mclane http://geoffair.net/mperl
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Cwd;
my $os = $^O;
my $perl_dir = '/home/geoff/bin';
my $PATH_SEP = '/';
my $temp_dir = '/tmp';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $temp_dir = $perl_dir;
    $PATH_SEP = "\\";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n";
# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt";
open_log($outfile);

# user variables
my $VERS = "0.0.5 2015-01-09";
my $load_log = 0;
my $in_file = '';
my $verbosity = 0;
my $out_file = '';
my $src_dir = '';

# ### DEBUG ###
my $debug_on = 0;
my $def_file = 'F:\Projects\openssl-1.0.2c\bldlog-1.txt';

### program variables
my @warnings = ();
my $cwd = cwd();

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        prt( "\nNo warnings issued.\n\n" ) if (VERB9());
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    close_log($outfile,$load_log);
    exit($val);
}


sub prtw($) {
   my ($tx) = shift;
   $tx =~ s/\n$//;
   prt("$tx\n");
   push(@warnings,$tx);
}

my %defines = ();
my %include = ();

sub get_source($) {
    my $line = shift;
    $line = trim_all($line);
    my @arr = space_split($line);
    my ($itm,$cnt,$i,$i2);
    $cnt = scalar @arr;
    my $src = '';
    for ($i = 0; $i < $cnt; $i++) {
        $i2 = $i + 1;
        $itm = $arr[$i];
        if ($itm eq 'cl') {
        } elsif ($itm =~ /^\/F/) {
        } elsif ($itm =~ /^-D/) {
            $itm = substr($itm,2);
            $defines{$itm} = 1;
        } elsif ($itm =~ /^-I/) {
            $itm = substr($itm,2);
            $itm = path_d2u($itm);
            $include{$itm} = 1;
        } elsif ($itm =~ /^\/M/) {
        } elsif ($itm =~ /^\/O/) {
        } elsif ($itm =~ /^-W/) {
        } elsif ($itm =~ /^-G/) {
        } elsif ($itm =~ /^-Z/) {
        } elsif ($itm =~ /^\/Z/) {
        } elsif ($itm =~ /^-nologo/) {
        } elsif ($itm eq '-c') {
            if ($i2 < $cnt) {
                $i++;
                $src = $arr[$i];
            }
        } else {
            pgm_exit(1,"Unknown compile flag '$itm' *** FIX ME ***\n");
        }
    }
    return $src;
}


sub get_output($) {
    my $line = shift;
    $line = trim_all($line);
    my @arr = space_split($line);
    my ($itm,$cnt,$i,$i2);
    $cnt = scalar @arr;
    my $src = '';
    for ($i = 0; $i < $cnt; $i++) {
        $i2 = $i + 1;
        $itm = $arr[$i];
        if ($itm eq 'link') {
        } elsif ($itm =~ /^\/nologo/) {
        } elsif ($itm =~ /^\/opt/) {
        } elsif ($itm =~ /^\/debug/) {
        } elsif ($itm =~ /^\@/) {
        } elsif ($itm =~ /^\/subsystem:/) {
        } elsif ($itm =~ /^\/dll/) {
        } elsif ($itm =~ /^\/def:/) {
        } elsif ($itm =~ /^\/out:/) {
            $src = substr($itm,5);
        } else {
            pgm_exit(1,"Unknown link flag '$itm' *** FIX ME ***\n");
        }

    }
    return $src;
}

sub get_resource($) {
    my $line = shift;
    $line = trim_all($line);
    my @arr = space_split($line);
    my ($itm,$cnt,$i,$i2);
    $cnt = scalar @arr;
    my $src = $arr[-1];
    return $src;
}

sub get_cmake_header($) {
    my $title = shift;
    my $cmake = "# ============================================================\n";
    $cmake .= "# $src_dir\\"."CMakeLists.txt, generated by $pgmname, $VERS, on ";
    $cmake .= lu_get_YYYYMMDD_hhmmss(time())."\n";
    $cmake .= "# from build log $in_file\n";
    $cmake .= "cmake_minimum_required( VERSION 2.8.8 )\n\n";
    ###$cmake .= get_includes_block();
    $cmake .= "project($title)\n\n";
    return $cmake;
}

sub add_options_block($) {
    my $type = shift;
    my $txt = <<EOF;
# Allow developers to select if Dynamic or static libraries are built.
set( LIB_TYPE STATIC )  # set default static
option( BUILD_SHARED_LIB    "Set ON to build Shared Libraries"      $type )
option( BUILD_TEST_PROGRAMS "Set ON to build the utility programs"  OFF )

# read 'version' file into a variable (stripping any newlines or spaces)
#file(READ version versionFile)
#if (NOT versionFile)
#    message(FATAL_ERROR "Unable to determine version. version file is missing.")
#endif()
#string(STRIP "\${versionFile}" MY_VERSION)
#add_definitions( -DVERSION="\${MY_VERSION}" )

# Uncomment to REDUCE the Windows configurations buildable
# set(CMAKE_CONFIGURATION_TYPES "Release;Debug" CACHE STRING "" FORCE) # Disables MinSizeRel & MaxSpeedRel

EOF
    return $txt;
}

sub get_definitions_block() {
    my $txt = <<EOF;
if(CMAKE_COMPILER_IS_GNUCXX)
    set( WARNING_FLAGS -Wall )
endif(CMAKE_COMPILER_IS_GNUCXX)

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 
   set( WARNING_FLAGS "-Wall -Wno-overloaded-virtual" )
endif() 

if(WIN32 AND MSVC)
    # turn off various warnings
    set(WARNING_FLAGS "\${WARNING_FLAGS} /wd4996")
    # foreach(warning 4244 4251 4267 4275 4290 4786 4305)
    #     set(WARNING_FLAGS "\${WARNING_FLAGS} /wd\${warning}")
    # endforeach(warning)
    set( MSVC_FLAGS "-DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS" )
    # if (\${MSVC_VERSION} EQUAL 1600)
    #    set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
    # endif (\${MSVC_VERSION} EQUAL 1600)
    # set( NOMINMAX 1 )
    list(APPEND extra_LIBS Winmm.lib)
    # to distinguish between debug and release lib
    set( CMAKE_DEBUG_POSTFIX "d" )
endif()

set( CMAKE_C_FLAGS "\${CMAKE_C_FLAGS} \${WARNING_FLAGS} \${MSVC_FLAGS} -D_REENTRANT" )
set( CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} \${WARNING_FLAGS} \${MSVC_FLAGS} -D_REENTRANT" )
set( CMAKE_EXE_LINKER_FLAGS "\${CMAKE_EXE_LINKER_FLAGS} \${MSVC_LD_FLAGS}" )

# example header checks
# check_include_file(inttypes.h     HAVE_INTTYPES_H)
# example function checks
# check_function_exists(_snprintf   HAVE__SNPRINTF)
# add these definition to config.h.in
#cmakedefine HAVE_INTTYPES_H 1
#cmakedefine HAVE__SNPRINTF 1

# then put information in a config.h file
#configure_file( "\${CMAKE_SOURCE_DIR}/config.h.in" "\${CMAKE_BINARY_DIR}/config.h" )
# make config.h findable
#include_directories( \${CMAKE_BINARY_DIR} )
# and usable
#add_definitions( -DHAVE_CONFIG_H )

if(BUILD_SHARED_LIB)
   set(LIB_TYPE SHARED)
   message(STATUS "*** Building DLL library \${LIB_TYPE}")
else(BUILD_SHARED_LIB)
   message(STATUS "*** Building static library \${LIB_TYPE}")
endif(BUILD_SHARED_LIB)

EOF
    return $txt;
}



sub process_in_file($) {
    my ($inf) = @_;
    if (! open INF, "<$inf") {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    my @lines = <INF>;
    close INF;
    my $lncnt = scalar @lines;
    prt("Processing $lncnt lines, from [$inf]...\n");
    my ($line,$inc,$lnn,$src);
    ($lnn, $src_dir) = fileparse($inf);
    $lnn = 0;
    my @srcs = ();
    my %links = ();
    foreach $line (@lines) {
        chomp $line;
        $lnn++;
        if (($line =~ /^\s*cl\s+/) && ($line =~ /\s+-c\s+/)) {
            $src = get_source($line);
            if (length($src)) {
                prt("$lnn: $src\n") if (VERB5());
                push(@srcs,$src);
            } else {
                pgm_exit(1,"Failed src '$line'");
            }
        } elsif ($line =~ /^\s*rc\s+/) {
            $src = get_resource($line);
            #prt("$lnn: $line\n");
            if (length($src)) {
                prt("$lnn: $src\n") if (VERB5());
                push(@srcs,$src);
            } else {
                pgm_exit(1,"Failed resource '$line'");
            }
        } elsif ($line =~ /^\s*link\s+/) {
            $src = get_output($line);
            ##prt("$lnn: $line\n");
            if (length($src)) {
                prt("$lnn: $src\n") if (VERB5());
                my @a = @srcs;
                $links{$src} = \@a;
                @srcs = ();
            } else {
                pgm_exit(1,"Failed output '$line'");
            }
        }
    }
    my @arr = sort keys %links;
    my ($key,$ra,$n,$d,$e,$nm,$dr,$scnt);
    my $cmake = '';
    my @libs = ();
    my @exes = ();
    foreach $key (@arr) {
        $ra = $links{$key};
        ($n,$d,$e) = fileparse($key, qr/\.[^.]*/);
        if ($e eq '.dll') {
            push(@libs,$key);
        } elsif ($e eq '.exe') {
            push(@exes,$key);
        } else {
            pgm_exit(1,"Unhandled extent '$e' *** FIX ME ***\n");
        }
    }
    $scnt = scalar @libs;
    $cmake .= "\n";
    $cmake .= "##################################\n";
    $cmake .= "######## $scnt LIBRARIES #########\n";
    $cmake .= "##################################\n";
    foreach $key (@libs) {
        $ra = $links{$key};
        $key = path_d2u($key);
        ($n,$d,$e) = fileparse($key, qr/\.[^.]*/);
        ##$cmake .= "set(name $key)\n";
        $cmake .= "\n";
        $cmake .= "set(name $n)\n";
        $cmake .= "set( \${name}_SRCS\n";
        @srcs = ();
        $scnt = scalar @{$ra};
        foreach $src (@{$ra}) {
            $src = path_d2u($src);
            $src =~ s/^\.\///;
            push(@srcs,$src);
        }
        foreach $src (sort @srcs) {
            $cmake .= "    $src\n";
        }
        $cmake .= "    )\n";
        if ($e eq '.dll') {
            $cmake .= "add_library( \${name} \${LIB_TYPE} \${\${name}_SRCS} )\n";
            $cmake .= "list(APPEND add_LIBS \${name})\n";
        } elsif ($e eq '.exe') {
            $cmake .= "add_executable( \${name} \${\${name}_SRCS} )\n";
        } else {
            pgm_exit(1,"Unhandled extent '$e' *** FIX ME ***\n");
        }
    }
    $scnt = scalar @exes;
    $cmake .= "\n";
    $cmake .= "##################################\n";
    $cmake .= "######## $scnt EXECUTABLE ########\n";
    $cmake .= "##################################\n";
    foreach $key (@exes) {
        $ra = $links{$key};
        $key = path_d2u($key);
        ($n,$d,$e) = fileparse($key, qr/\.[^.]*/);
        ##$cmake .= "set(name $key)\n";
        $cmake .= "\n";
        $cmake .= "set(name $n)\n";
        $cmake .= "set( \${name}_SRCS\n";
        @srcs = ();
        $scnt = scalar @{$ra};
        foreach $src (@{$ra}) {
            $src = path_d2u($src);
            $src =~ s/^\.\///;
            push(@srcs,$src);
        }
        foreach $src (sort @srcs) {
            $cmake .= "    $src\n";
        }
        $cmake .= "    )\n";
        if ($e eq '.dll') {
            $cmake .= "add_library( \${name} \${LIB_TYPE} \${\${name}_SRCS} )\n";
        } elsif ($e eq '.exe') {
            $cmake .= "add_executable( \${name} \${\${name}_SRCS} )\n";
            $cmake .= "target_link_libraries( \${name} \${add_LIBS} )\n";
        } else {
            pgm_exit(1,"Unhandled extent '$e' *** FIX ME ***\n");
        }
    }
    @arr = sort keys %defines;
    my $tmp = get_cmake_header('libssl');
    $tmp .= add_options_block('OFF');
    $tmp .= get_definitions_block();
    foreach $src (@arr) {
        $tmp .= "add_definitions( -D$src )\n";
    }
    @arr = sort keys %include;
    foreach $src (@arr) {
        $tmp .= "include_directories( $src )\n";
    }
    $cmake = $tmp.$cmake;
    if (length($out_file)) {
        rename_2_old_bak($out_file);
        write2file($cmake,$out_file);
        prt("CMake script written to $out_file\n");
    } else {
        prt($cmake);
        $load_log = 1;
    }
}

#########################################
### MAIN ###
parse_args(@ARGV);
process_in_file($in_file);
pgm_exit(0,"");
########################################

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av);
}

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg);
    my $verb = VERB2();
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                $verb = VERB2();
                prt("Verbosity = $verbosity\n") if ($verb);
            } elsif ($sarg =~ /^l/) {
                if ($sarg =~ /^ll/) {
                    $load_log = 2;
                } else {
                    $load_log = 1;
                }
                prt("Set to load log at end. ($load_log)\n") if ($verb);
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $out_file = $sarg;
                prt("Set out file to [$out_file].\n") if ($verb);
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = $arg;
            prt("Set input to [$in_file]\n") if ($verb);
        }
        shift @av;
    }

    if ($debug_on) {
        prtw("WARNING: DEBUG is ON!\n");
        if (length($in_file) ==  0) {
            $in_file = $def_file;
            prt("Set DEFAULT input to [$in_file]\n");
        }
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n");
    }
}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --out <file>  (-o) = Write output to this file.\n");
}

# eof - template.pl
