#!/usr/bin/env perl
#
# Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# ===
#
# make2qmake
#
# Parses the autoconf/automake artifacts throughout the source tree
# tree and creates a set of 'qmake' files alongside the source.
#
# Run from the project's top directory after running 'configure'
# then create a build directory and run qmake (or qmake6) from
# there.
#
# Unix:
#   $ ./configure --with-whatever CXXFLAGS="-X"
#   $ libexec/make2qmake
#   $ mkdir build
#   $ cd build
#   $ qmake ..
#   $ make
#
# Windows:
#   $ perl libexec/make2qmake --static-gui
#   $ mkdir build
#   $ cd build
#   $ c:\qt\bin\qmake ..
#   $ nmake /u VERBOSE=1 MBEDTLS_INC=... MBEDTLS_DLIB=... MBEDTLS_RLIB=... all
#
# See also: make2cmake, winbuild.pl
#

use strict ;
use Getopt::Long ;
use File::Basename ;
use FileHandle ;
use lib dirname($0) ;
use BuildInfo ;

package qmake ;

our $cfg_static_gui = 0 ;

sub create_qmake_file
{
	my ( $m ) = @_ ; # see BuildInfo.pm

	if( (scalar(@{$m->{e_libraries}})+scalar(@{$m->{e_programs}})) == 1 )
	{
		map { do_library( $m , $_ ) } @{$m->{e_libraries}} ;
		map { do_program( $m , $_ ) } @{$m->{e_programs}} ;
	}
	elsif( (scalar(@{$m->{e_libraries}})+scalar(@{$m->{e_programs}})) > 1 )
	{
		my @subdirs = () ;
		map { do_sub_library( \@subdirs , $m , $_ ) } @{$m->{e_libraries}} ;
		map { do_sub_program( \@subdirs , $m , $_ ) } @{$m->{e_programs}} ;
		do_subdirs( $m , \@subdirs ) ;
	}
	else
	{
		do_subdirs( $m ) ;
	}
}

sub do_subdirs
{
	my ( $m , $subdirs_ref ) = @_ ;
	my @subdirs = $subdirs_ref ? @$subdirs_ref : @{$m->{e_subdirs}} ;
	_log( "qmake-file=[$$m{e_qmake_out}]" ) ;
	my $fh = new FileHandle( $m->{e_qmake_out} , "w" ) or die "error: cannot create .pro file [$$m{e_qmake_out}]: $!\n" ;
	print $fh "# $$m{e_qmake_out} -- generated by $0\n" ;
	print $fh "TEMPLATE = subdirs\n" ;
	print $fh "CONFIG += ordered\n" ;
	print $fh "CONFIG += debug_and_release\n" ;
	print $fh "SUBDIRS = " , join(" ",@subdirs) , "\n" ;
	$fh->close() or die ;
}

sub do_library
{
	my ( $m , $libkey , $pro_file , $up ) = @_ ;
	$up ||= "." ;
	$pro_file ||= $m->{e_qmake_out} ;
	my $e = $m->{e_library}->{$libkey} ;
	_log( "qmake-file=[$pro_file]" ) ;
	my $fh = new FileHandle( $pro_file , "w" ) or die "error: cannot create .pro file [$pro_file]: $!\n" ;

	my @definitions = @{$e->{definitions}} ;
	push @definitions , "G_QT_STATIC" if $cfg_static_gui ;
	my @includes = map { "$up/$_" } @{$e->{includes}} ;
	if( $m->{e_is_windows} )
	{
		push @includes , '$(MBEDTLS_INC)/.' if $e->{need_mbedtls_inc} ;
		push @includes , '$(OPENSSL_INC)/.' if $e->{need_openssl_inc} ;
		#push @includes , "..." if $e->{need_qt_inc} ; # not needed (via qmake)
	}
	print $fh "# $pro_file -- generated by $0\n" ;
	print $fh "# library: $$e{libname}\n" ;
	print $fh "TEMPLATE = lib\n" ;
	print $fh "TARGET = $$e{libname}\n" ;
	print $fh "CONFIG += staticlib\n" ;
	print $fh "CONFIG += warn_on\n" ;
	print $fh "CONFIG += debug_and_release\n" ;
	print $fh "CONFIG -= qt\n" ;
	print $fh "CONFIG += thread\n" ;
	print $fh "CONFIG += $$m{e_compile_stdcxx}\n" if $m->{e_compile_stdcxx} ;
	if( @includes )
	{
		print $fh "INCLUDEPATH = \\\n    " , join(" \\\n    ",@includes) , "\n" ;
	}
	if( @{$e->{definitions}} )
	{
		print $fh "DEFINES = " , join(" ",@definitions) , "\n" ;
	}
	print $fh "CONFIG(debug, debug|release) {\n" ;
	print $fh "    DEFINES += G_WITH_DEBUG\n" ;
	print $fh "    DEFINES -= G_LIB_SMALL\n" ;
	print $fh "}\n" ;
	my @sources = map { "$up/$_" } @{$e->{sources}} ;
	if( @sources )
	{
		print $fh "SOURCES = \\\n    " , join(" \\\n    ",@sources) , "\n" ;
		print $fh "CONFIG(debug, debug|release) {\n" ;
		print $fh "    win32:QMAKE_POST_LINK = mkdir $up\\debug 2>NUL: |echo. && copy /y debug\\$$e{libfile} $up\\debug\n" if( $up eq ".." ) ;
		print $fh "    unix:QMAKE_POST_LINK = mkdir $up/debug 2>/dev/null ; mv -f $$e{libfile} $up/debug/\n" ;
		print $fh "} else {\n" ;
		print $fh "    win32:QMAKE_POST_LINK = mkdir $up\\release 2>NUL: |echo. && copy /y release\\$$e{libfile} $up\\release\n" if( $up eq ".." ) ;
		print $fh "    unix:QMAKE_POST_LINK = mkdir $up/release 2>/dev/null ; mv -f $$e{libfile} $up/release/\n" ;
		print $fh "}\n" ;
	}
	$fh->close() or die ;
}

sub do_sub_library
{
	my ( $out , $m , $libkey ) = @_ ;
	my $e = $m->{e_library}->{$libkey} ;
	my $pro_d = "$$m{e_dir}/lib_$$e{libname}" ;
	push @$out , "lib_$$e{libname}" ;
	mkdir $pro_d ;
	do_library( $m , $libkey , "$pro_d/lib_$$e{libname}.pro" , ".." ) ;
}

sub do_program
{
	my ( $m , $progkey , $pro_file , $up ) = @_ ;
	$pro_file ||= $m->{e_qmake_out} ;
	$up ||= "." ;
	my $e = $m->{e_program}->{$progkey} ;

	_log( "qmake-file=[$pro_file]" ) ;
	my $fh = new FileHandle( $pro_file , "w" ) or die "error: cannot create .pro file [$pro_file]: $!\n" ;

	my @definitions = @{$e->{definitions}} ;
	push @definitions , "G_QT_STATIC" if $cfg_static_gui ;
	my @includes = map { $_ =~ m;^/; ? $_ : "$up/$_" } @{$e->{includes}} ;
	if( $m->{e_is_windows} )
	{
		push @includes , '$(MBEDTLS_INC)/.' if $e->{need_mbedtls_inc} ;
		push @includes , '$(OPENSSL_INC)/.' if $e->{need_openssl_inc} ;
		#push @includes , "..." if $e->{need_qt_inc} ; # not needed (via qmake)
	}
	my $rc_includes = join(" ",map {"$up/$_"} @includes) =~ s;/;\\;gr =~ s;\\$;;r ;
	my @sources = map { "$up/$_" } @{$e->{sources}} ;

	# qmake does /manifest:embed and /manifestdependency=<commoncontrols> --
	# we ignore our own manifest file and just add /manifestuac -- see also
	# 'CONFIG' option 'embed_manifest_exe'
	#
	my @link_options = @{$e->{link_options}} ;
	push @link_options , $e->{uac_option} if $e->{uac_option} ;

	my @debug_libs = () ;
	my @release_libs = () ;
	if( $m->{e_is_windows} )
	{
		@debug_libs = map {my ($d,$n)=@$_; "$up/$d/debug/$n.lib" } @{$e->{our_libpairs}} ;
		@release_libs = map {my ($d,$n)=@$_; "$up/$d/release/$n.lib" } @{$e->{our_libpairs}} ;
		push @debug_libs , map { "\$(MBEDTLS_DLIB)/$_.lib" } @{$e->{mbedtls_libnames}} if $e->{need_mbedtls_libs} ;
		push @debug_libs , map { "lib$_.lib" } @{$e->{openssl_libnames}} if $e->{need_openssl_libs} ;
		push @debug_libs , map { "$_.lib" } @{$e->{sys_libnames}} ;
		push @release_libs , map { "\$(MBEDTLS_RLIB)/$_.lib" } @{$e->{mbedtls_libnames}} if $e->{need_mbedtls_libs} ;
		push @release_libs , map { "lib$_.lib" } @{$e->{openssl_libnames}} if $e->{need_openssl_libs} ;
		push @release_libs , map { "$_.lib" } @{$e->{sys_libnames}} ;
	}
	else
	{
		@debug_libs = map {my ($d,$n)=@$_; "$up/$d/debug/lib$n.a" } @{$e->{our_libpairs}} ;
		@release_libs = map {my ($d,$n)=@$_; "$up/$d/release/lib$n.a" } @{$e->{our_libpairs}} ;
		push @debug_libs , map { "-l$_" } @{$e->{mbedtls_libnames}} if $e->{need_mbedtls_libs} ;
		push @debug_libs , map { "-l$_" } @{$e->{openssl_libnames}} if $e->{need_openssl_libs} ;
		push @debug_libs , map { "-l$_" } @{$e->{sys_libnames}} ;
		push @release_libs , map { "-l$_" } @{$e->{mbedtls_libnames}} if $e->{need_mbedtls_libs} ;
		push @release_libs , map { "-l$_" } @{$e->{openssl_libnames}} if $e->{need_openssl_libs} ;
		push @release_libs , map { "-l$_" } @{$e->{sys_libnames}} ;
	}

	print $fh "# $pro_file -- generated by $0\n" ;
	print $fh "# program: $$e{progname}\n" ;
	print $fh "TEMPLATE = app\n" ;
	print $fh "TARGET = $$e{progname}\n" ;
	print $fh "CONFIG += warn_on\n" ;
	print $fh "CONFIG += debug_and_release\n" ;
	print $fh "CONFIG += $$m{e_compile_stdcxx}\n" if $m->{e_compile_stdcxx} ;
	if( $e->{need_qt_libs} )
	{
		print $fh "QT = widgets gui core\n" ;
	}
	else
	{
		print $fh "CONFIG -= qt\n" ;
		print $fh "CONFIG += thread\n" ;
		print $fh "CONFIG += " , ($e->{subsystem} eq "console" ? "cmdline" : "windows" ) , "\n" ;
		print $fh "QT -= widgets gui core\n" ;
	}
	if( $e->{messages_mc} )
	{
		print $fh "mc.output = messages.obj\n" ;
		print $fh "mc.commands = " ,
			'mc ${QMAKE_FILE_NAME} && ' ,
			'echo. 2> messages.c && ' ,
			'cl /nologo $(CXXFLAGS) /c messages.c >NUL:' , "\n" ;
		print $fh "mc.input = MESSAGES_MC\n" ;
		print $fh "QMAKE_EXTRA_COMPILERS += mc\n" ;
		print $fh "MESSAGES_MC = $up/" , $e->{messages_mc} , "\n" ;
	}
	print $fh "INCLUDEPATH = \\\n    " , join(" \\\n    ",@includes) , "\n" if @includes ;
	print $fh "DEFINES = " , join(" ",@definitions) , "\n" ;
	print $fh "RC_FILE = $up/$$e{rcfile}\n" if $e->{rcfile} ;
	print $fh "RC_INCLUDEPATH = $rc_includes\n" if $e->{rcfile} ;
	print $fh "HEADERS = " , join(" ",map { "$up/$_" } @{$e->{moc_in}}) , "\n" if @{$e->{moc_in}} ;
	print $fh "SOURCES = \\\n    " , join(" \\\n    ",@sources) , "\n" ;
	print $fh "QMAKE_LFLAGS += " , join(" ",@link_options) , "\n" if @link_options ;
	print $fh "CONFIG(debug, debug|release) {\n" ;
	print $fh "    DEFINES += G_WITH_DEBUG\n" ;
	print $fh "    LIBS = \\\n        " , join(" \\\n        ",@debug_libs) , "\n" ;
	#print $fh "    win32:QMAKE_POST_LINK = mkdir debug 2>NUL: | echo. && copy /y $$e{progfile} debug/\n" ; # windows builds library in debug/
	print $fh "    unix:QMAKE_POST_LINK = test -d debug || mkdir debug && cp $$e{progfile} debug/\n" ; # unixs build library in cwd
	print $fh "} else {\n" ;
	print $fh "    LIBS = \\\n        " , join(" \\\n        ",@release_libs) , "\n" ;
	#print $fh "    win32:QMAKE_POST_LINK = mkdir release 2>NUL: | echo. && copy /y $$e{progfile} release/\n" ;
	print $fh "    unix:QMAKE_POST_LINK = test -d release || mkdir release && cp $$e{progfile} release/\n" ;
	print $fh "}\n" ;
	$fh->close() or die ;
}

sub do_sub_program
{
	my ( $out , $m , $progkey , $qt_config ) = @_ ;
	my $e = $m->{e_program}->{$progkey} ;
	my $target = $e->{progname} ;
	my $pro_d = join( "/" , File::Basename::dirname($m->path()) , "exe_$$e{progname}" ) ;
	push @$out , "exe_$target" ;
	mkdir $pro_d ;
	do_program( $m , $progkey , "$pro_d/exe_$$e{progname}.pro" , ".." ) ;
}

sub _log
{
	print File::Basename::basename($0) , ": " , @_ , "\n" ;
}

# ==

package main ;

if( basename($0) eq "make2qmake" )
{
	my %opt = () ;
	if( !GetOptions( \%opt , "help|h" , "no-mbedtls" ,
		"no-gui" , "static-gui|s" , "dump" , "dump-all" ) || $opt{help} )
	{
		my $error = $opt{help} ? "" : "error: " ;
		print "${error}usage: make2qmake [<options>]\n" ;
		print "          --no-mbedtls     without mbedtls\n" ;
		print "          --no-gui         without qt, no gui\n" ;
		print "          --static-gui     gui statically linked to qt\n" ;
		print "          --debug-dump\n" ;
		print "          --debug-dump-all\n" ;
		exit( $error ? 1 : 0 ) ;
	}

	$qmake::cfg_static_gui = $opt{'static-gui'} ;
	my %makefiles_opt = (
		windows_mbedtls => !$opt{'no-mbedtls'} ,
		windows_openssl => 0 ,
		windows_gui => !$opt{'no-gui'} ,
		qt_version => undef , # qmake knows
		verbose => 0 , # AutoMakeParser::readall()
	) ;

	my @m = BuildInfo::read_makefiles( "." , "make2qmake: " , \%makefiles_opt ) ;
	if( $opt{dump} ) { BuildInfo::dump( @m ) ; exit(0) }
	if( $opt{'dump-all'} ) { BuildInfo::dumpall( @m ) ; exit(0) }
	for my $m ( @m )
	{
		qmake::create_qmake_file( $m ) ;
	}
}
else
{
	1 ;
}

