postgresql/src/tools/msvc/Mkvcbuild.pm

924 lines
28 KiB
Perl
Raw Normal View History

package Mkvcbuild;
#
# Package that generates build files for msvc build
#
2010-09-20 22:08:53 +02:00
# src/tools/msvc/Mkvcbuild.pm
#
use Carp;
use Win32;
use strict;
use warnings;
use Project;
use Solution;
2010-01-09 15:45:47 +01:00
use Cwd;
use File::Copy;
use Config;
use VSObjectFactory;
use List::Util qw(first);
use Exporter;
our (@ISA, @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT_OK = qw(Mkvcbuild);
my $solution;
my $libpgport;
my $libpgcommon;
my $libpgfeutils;
my $postgres;
my $libpq;
# Set of variables for modules in contrib/ and src/test/modules/
my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' };
2015-05-24 03:35:49 +02:00
my @contrib_uselibpq = ('dblink', 'oid2name', 'postgres_fdw', 'vacuumlo');
my @contrib_uselibpgport = ('oid2name', 'pg_standby', 'vacuumlo');
my @contrib_uselibpgcommon = ('oid2name', 'pg_standby', 'vacuumlo');
my $contrib_extralibs = undef;
2017-05-18 01:01:23 +02:00
my $contrib_extraincludes = { 'dblink' => ['src/backend'] };
my $contrib_extrasource = {
'cube' => [ 'contrib/cube/cubescan.l', 'contrib/cube/cubeparse.y' ],
2015-05-24 03:35:49 +02:00
'seg' => [ 'contrib/seg/segscan.l', 'contrib/seg/segparse.y' ], };
my @contrib_excludes = (
'commit_ts', 'hstore_plperl',
'hstore_plpython', 'intagg',
'ltree_plpython', 'pgcrypto',
'sepgsql', 'brin',
'test_extensions', 'test_pg_dump',
'snapshot_too_old');
# Set of variables for frontend modules
my $frontend_defines = { 'initdb' => 'FRONTEND' };
Change pg_ctl to detect server-ready by watching status in postmaster.pid. Traditionally, "pg_ctl start -w" has waited for the server to become ready to accept connections by attempting a connection once per second. That has the major problem that connection issues (for instance, a kernel packet filter blocking traffic) can't be reliably told apart from server startup issues, and the minor problem that if server startup isn't quick, we accumulate "the database system is starting up" spam in the server log. We've hacked around many of the possible connection issues, but it resulted in ugly and complicated code in pg_ctl.c. In commit c61559ec3, I changed the probe rate to every tenth of a second. That prompted Jeff Janes to complain that the log-spam problem had become much worse. In the ensuing discussion, Andres Freund pointed out that we could dispense with connection attempts altogether if the postmaster were changed to report its status in postmaster.pid, which "pg_ctl start" already relies on being able to read. This patch implements that, teaching postmaster.c to report a status string into the pidfile at the same state-change points already identified as being of interest for systemd status reporting (cf commit 7d17e683f). pg_ctl no longer needs to link with libpq at all; all its functions now depend on reading server files. In support of this, teach AddToDataDirLockFile() to allow addition of postmaster.pid lines in not-necessarily-sequential order. This is needed on Windows where the SHMEM_KEY line will never be written at all. We still have the restriction that we don't want to truncate the pidfile; document the reasons for that a bit better. Also, fix the pg_ctl TAP tests so they'll notice if "start -w" mode is broken --- before, they'd just wait out the sixty seconds until the loop gives up, and then report success anyway. (Yes, I found that out the hard way.) While at it, arrange for pg_ctl to not need to #include miscadmin.h; as a rather low-level backend header, requiring that to be compilable client-side is pretty dubious. This requires moving the #define's associated with the pidfile into a new header file, and moving PG_BACKEND_VERSIONSTR someplace else. For lack of a clearly better "someplace else", I put it into port.h, beside the declaration of find_other_exec(), since most users of that macro are passing the value to find_other_exec(). (initdb still depends on miscadmin.h, but at least pg_ctl and pg_upgrade no longer do.) In passing, fix main.c so that PG_BACKEND_VERSIONSTR actually defines the output of "postgres -V", which remarkably it had never done before. Discussion: https://postgr.es/m/CAMkU=1xJW8e+CTotojOMBd-yzUvD0e_JZu2xHo=MnuZ4__m7Pg@mail.gmail.com
2017-06-28 23:31:24 +02:00
my @frontend_uselibpq = ('pg_upgrade', 'pgbench', 'psql', 'initdb');
2015-05-24 03:35:49 +02:00
my @frontend_uselibpgport = (
'pg_archivecleanup', 'pg_test_fsync',
'pg_test_timing', 'pg_upgrade',
2017-05-18 01:01:23 +02:00
'pg_waldump', 'pgbench');
2015-05-24 03:35:49 +02:00
my @frontend_uselibpgcommon = (
'pg_archivecleanup', 'pg_test_fsync',
'pg_test_timing', 'pg_upgrade',
2017-05-18 01:01:23 +02:00
'pg_waldump', 'pgbench');
my $frontend_extralibs = {
'initdb' => ['ws2_32.lib'],
'pg_restore' => ['ws2_32.lib'],
'pgbench' => ['ws2_32.lib'],
'psql' => ['ws2_32.lib'] };
my $frontend_extraincludes = {
'initdb' => ['src/timezone'],
'psql' => ['src/backend'] };
my $frontend_extrasource = {
'psql' => ['src/bin/psql/psqlscanslash.l'],
'pgbench' =>
[ 'src/bin/pgbench/exprscan.l', 'src/bin/pgbench/exprparse.y' ] };
2015-05-24 03:35:49 +02:00
my @frontend_excludes = (
2017-05-18 01:01:23 +02:00
'pgevent', 'pg_basebackup', 'pg_rewind', 'pg_dump',
'pg_waldump', 'scripts');
sub mkvcbuild
{
our $config = shift;
chdir('../../..') if (-d '../msvc' && -d '../../../src');
die 'Must run from root or msvc directory'
unless (-d 'src/tools/msvc' && -d 'src');
my $vsVersion = DetermineVisualStudioVersion();
$solution = CreateSolution($vsVersion, $config);
our @pgportfiles = qw(
chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
Replace PostmasterRandom() with a stronger source, second attempt. This adds a new routine, pg_strong_random() for generating random bytes, for use in both frontend and backend. At the moment, it's only used in the backend, but the upcoming SCRAM authentication patches need strong random numbers in libpq as well. pg_strong_random() is based on, and replaces, the existing implementation in pgcrypto. It can acquire strong random numbers from a number of sources, depending on what's available: - OpenSSL RAND_bytes(), if built with OpenSSL - On Windows, the native cryptographic functions are used - /dev/urandom Unlike the current pgcrypto function, the source is chosen by configure. That makes it easier to test different implementations, and ensures that we don't accidentally fall back to a less secure implementation, if the primary source fails. All of those methods are quite reliable, it would be pretty surprising for them to fail, so we'd rather find out by failing hard. If no strong random source is available, we fall back to using erand48(), seeded from current timestamp, like PostmasterRandom() was. That isn't cryptographically secure, but allows us to still work on platforms that don't have any of the above stronger sources. Because it's not very secure, the built-in implementation is only used if explicitly requested with --disable-strong-random. This replaces the more complicated Fortuna algorithm we used to have in pgcrypto, which is unfortunate, but all modern platforms have /dev/urandom, so it doesn't seem worth the maintenance effort to keep that. pgcrypto functions that require strong random numbers will be disabled with --disable-strong-random. Original patch by Magnus Hagander, tons of further work by Michael Paquier and me. Discussion: https://www.postgresql.org/message-id/CAB7nPqRy3krN8quR9XujMVVHYtXJ0_60nqgVc6oUk8ygyVkZsA@mail.gmail.com Discussion: https://www.postgresql.org/message-id/CAB7nPqRWkNYRRPJA7-cF+LfroYV10pvjdz6GNvxk-Eee9FypKA@mail.gmail.com
2016-12-05 12:42:59 +01:00
pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
Revert error-throwing wrappers for the printf family of functions. This reverts commit 16304a013432931e61e623c8d85e9fe24709d9ba, except for its changes in src/port/snprintf.c; as well as commit cac18a76bb6b08f1ecc2a85e46c9d2ab82dd9d23 which is no longer needed. Fujii Masao reported that the previous commit caused failures in psql on OS X, since if one exits the pager program early while viewing a query result, psql sees an EPIPE error from fprintf --- and the wrapper function thought that was reason to panic. (It's a bit surprising that the same does not happen on Linux.) Further discussion among the security list concluded that the risk of other such failures was far too great, and that the one-size-fits-all approach to error handling embodied in the previous patch is unlikely to be workable. This leaves us again exposed to the possibility of the type of failure envisioned in CVE-2015-3166. However, that failure mode is strictly hypothetical at this point: there is no concrete reason to believe that an attacker could trigger information disclosure through the supposed mechanism. In the first place, the attack surface is fairly limited, since so much of what the backend does with format strings goes through stringinfo.c or psprintf(), and those already had adequate defenses. In the second place, even granting that an unprivileged attacker could control the occurrence of ENOMEM with some precision, it's a stretch to believe that he could induce it just where the target buffer contains some valuable information. So we concluded that the risk of non-hypothetical problems induced by the patch greatly outweighs the security risks. We will therefore revert, and instead undertake closer analysis to identify specific calls that may need hardening, rather than attempt a universal solution. We have kept the portion of the previous patch that improved snprintf.c's handling of errors when it calls the platform's sprintf(). That seems to be an unalloyed improvement. Security: CVE-2015-3166
2015-05-20 00:14:52 +02:00
sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c
win32env.c win32error.c win32security.c win32setlocale.c);
push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
if ($vsVersion >= '9.00')
{
push(@pgportfiles, 'pg_crc32c_choose.c');
push(@pgportfiles, 'pg_crc32c_sse42.c');
push(@pgportfiles, 'pg_crc32c_sb8.c');
}
else
{
2015-05-24 03:35:49 +02:00
push(@pgportfiles, 'pg_crc32c_sb8.c');
}
our @pgcommonallfiles = qw(
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677). This introduces a new generic SASL authentication method, similar to the GSS and SSPI methods. The server first tells the client which SASL authentication mechanism to use, and then the mechanism-specific SASL messages are exchanged in AuthenticationSASLcontinue and PasswordMessage messages. Only SCRAM-SHA-256 is supported at the moment, but this allows adding more SASL mechanisms in the future, without changing the overall protocol. Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later. The SASLPrep algorithm, for pre-processing the password, is not yet implemented. That could cause trouble, if you use a password with non-ASCII characters, and a client library that does implement SASLprep. That will hopefully be added later. Authorization identities, as specified in the SCRAM-SHA-256 specification, are ignored. SET SESSION AUTHORIZATION provides more or less the same functionality, anyway. If a user doesn't exist, perform a "mock" authentication, by constructing an authentic-looking challenge on the fly. The challenge is derived from a new system-wide random value, "mock authentication nonce", which is created at initdb, and stored in the control file. We go through these motions, in order to not give away the information on whether the user exists, to unauthenticated users. Bumps PG_CONTROL_VERSION, because of the new field in control file. Patch by Michael Paquier and Heikki Linnakangas, reviewed at different stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev, and many others. Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
base64.c config_info.c controldata_utils.c exec.c ip.c keywords.c
md5.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
Use SASLprep to normalize passwords for SCRAM authentication. An important step of SASLprep normalization, is to convert the string to Unicode normalization form NFKC. Unicode normalization requires a fairly large table of character decompositions, which is generated from data published by the Unicode consortium. The script to generate the table is put in src/common/unicode, as well test code for the normalization. A pre-generated version of the tables is included in src/include/common, so you don't need the code in src/common/unicode to build PostgreSQL, only if you wish to modify the normalization tables. The SASLprep implementation depends on the UTF-8 functions from src/backend/utils/mb/wchar.c. So to use it, you must also compile and link that. That doesn't change anything for the current users of these functions, the backend and libpq, as they both already link with wchar.o. It would be good to move those functions into a separate file in src/commmon, but I'll leave that for another day. No documentation changes included, because there is no details on the SCRAM mechanism in the docs anyway. An overview on that in the protocol specification would probably be good, even though SCRAM is documented in detail in RFC5802. I'll write that as a separate patch. An important thing to mention there is that we apply SASLprep even on invalid UTF-8 strings, to support other encodings. Patch by Michael Paquier and me. Discussion: https://www.postgresql.org/message-id/CAB7nPqSByyEmAVLtEf1KxTRh=PWNKiWKEKQR=e1yGehz=wbymQ@mail.gmail.com
2017-04-07 13:56:05 +02:00
saslprep.c scram-common.c string.c unicode_norm.c username.c
wait_error.c);
if ($solution->{options}->{openssl})
{
push(@pgcommonallfiles, 'sha2_openssl.c');
}
else
{
push(@pgcommonallfiles, 'sha2.c');
}
2015-05-24 03:35:49 +02:00
our @pgcommonfrontendfiles = (
@pgcommonallfiles, qw(fe_memutils.c file_utils.c
2015-05-24 03:35:49 +02:00
restricted_token.c));
our @pgcommonbkndfiles = @pgcommonallfiles;
our @pgfeutilsfiles = qw(
mbprint.c print.c psqlscan.l psqlscan.c simple_list.c string_utils.c);
$libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
$libpgport->AddDefine('FRONTEND');
$libpgport->AddFiles('src/port', @pgportfiles);
$libpgcommon = $solution->AddProject('libpgcommon', 'lib', 'misc');
$libpgcommon->AddDefine('FRONTEND');
$libpgcommon->AddFiles('src/common', @pgcommonfrontendfiles);
$libpgfeutils = $solution->AddProject('libpgfeutils', 'lib', 'misc');
$libpgfeutils->AddDefine('FRONTEND');
$libpgfeutils->AddIncludeDir('src/interfaces/libpq');
$libpgfeutils->AddFiles('src/fe_utils', @pgfeutilsfiles);
$postgres = $solution->AddProject('postgres', 'exe', '', 'src/backend');
$postgres->AddIncludeDir('src/backend');
$postgres->AddDir('src/backend/port/win32');
$postgres->AddFile('src/backend/utils/fmgrtab.c');
$postgres->ReplaceFile(
'src/backend/port/dynloader.c',
'src/backend/port/dynloader/win32.c');
$postgres->ReplaceFile('src/backend/port/pg_sema.c',
'src/backend/port/win32_sema.c');
$postgres->ReplaceFile('src/backend/port/pg_shmem.c',
'src/backend/port/win32_shmem.c');
$postgres->AddFiles('src/port', @pgportfiles);
$postgres->AddFiles('src/common', @pgcommonbkndfiles);
$postgres->AddDir('src/timezone');
# We need source files from src/timezone, but that directory's resource
# file pertains to "zic", not to the backend.
$postgres->RemoveFile('src/timezone/win32ver.rc');
$postgres->AddFiles('src/backend/parser', 'scan.l', 'gram.y');
$postgres->AddFiles('src/backend/bootstrap', 'bootscanner.l',
'bootparse.y');
$postgres->AddFiles('src/backend/utils/misc', 'guc-file.l');
$postgres->AddFiles(
'src/backend/replication', 'repl_scanner.l',
'repl_gram.y', 'syncrep_scanner.l',
'syncrep_gram.y');
$postgres->AddDefine('BUILDING_DLL');
$postgres->AddLibrary('secur32.lib');
$postgres->AddLibrary('ws2_32.lib');
$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
$postgres->FullExportDLL('postgres.lib');
# The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
# if building without OpenSSL
if (!$solution->{options}->{openssl})
{
$postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c');
}
my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
'src/backend/snowball');
# This Makefile uses VPATH to find most source files in a subdirectory.
$snowball->RelocateFiles(
'src/backend/snowball/libstemmer',
sub {
return shift !~ /(dict_snowball.c|win32ver.rc)$/;
});
$snowball->AddIncludeDir('src/include/snowball');
$snowball->AddReference($postgres);
my $plpgsql =
$solution->AddProject('plpgsql', 'dll', 'PLs', 'src/pl/plpgsql/src');
$plpgsql->AddFiles('src/pl/plpgsql/src', 'pl_gram.y');
$plpgsql->AddReference($postgres);
if ($solution->{options}->{tcl})
{
my $found = 0;
my $pltcl =
$solution->AddProject('pltcl', 'dll', 'PLs', 'src/pl/tcl');
$pltcl->AddIncludeDir($solution->{options}->{tcl} . '/include');
$pltcl->AddReference($postgres);
for my $tclver (qw(86t 85 84))
{
my $tcllib = $solution->{options}->{tcl} . "/lib/tcl$tclver.lib";
if (-e $tcllib)
{
$pltcl->AddLibrary($tcllib);
$found = 1;
last;
}
}
die "Unable to find $solution->{options}->{tcl}/lib/tcl<version>.lib"
2017-05-18 01:01:23 +02:00
unless $found;
}
$libpq = $solution->AddProject('libpq', 'dll', 'interfaces',
'src/interfaces/libpq');
$libpq->AddDefine('FRONTEND');
$libpq->AddDefine('UNSAFE_STAT_OK');
$libpq->AddIncludeDir('src/port');
$libpq->AddLibrary('secur32.lib');
$libpq->AddLibrary('ws2_32.lib');
$libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
$libpq->UseDef('src/interfaces/libpq/libpqdll.def');
$libpq->ReplaceFile('src/interfaces/libpq/libpqrc.c',
'src/interfaces/libpq/libpq.rc');
$libpq->AddReference($libpgport);
# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677). This introduces a new generic SASL authentication method, similar to the GSS and SSPI methods. The server first tells the client which SASL authentication mechanism to use, and then the mechanism-specific SASL messages are exchanged in AuthenticationSASLcontinue and PasswordMessage messages. Only SCRAM-SHA-256 is supported at the moment, but this allows adding more SASL mechanisms in the future, without changing the overall protocol. Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later. The SASLPrep algorithm, for pre-processing the password, is not yet implemented. That could cause trouble, if you use a password with non-ASCII characters, and a client library that does implement SASLprep. That will hopefully be added later. Authorization identities, as specified in the SCRAM-SHA-256 specification, are ignored. SET SESSION AUTHORIZATION provides more or less the same functionality, anyway. If a user doesn't exist, perform a "mock" authentication, by constructing an authentic-looking challenge on the fly. The challenge is derived from a new system-wide random value, "mock authentication nonce", which is created at initdb, and stored in the control file. We go through these motions, in order to not give away the information on whether the user exists, to unauthenticated users. Bumps PG_CONTROL_VERSION, because of the new field in control file. Patch by Michael Paquier and Heikki Linnakangas, reviewed at different stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev, and many others. Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
# and sha2_openssl.c if building without OpenSSL, and remove sha2.c if
# building with OpenSSL.
if (!$solution->{options}->{openssl})
{
$libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c');
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677). This introduces a new generic SASL authentication method, similar to the GSS and SSPI methods. The server first tells the client which SASL authentication mechanism to use, and then the mechanism-specific SASL messages are exchanged in AuthenticationSASLcontinue and PasswordMessage messages. Only SCRAM-SHA-256 is supported at the moment, but this allows adding more SASL mechanisms in the future, without changing the overall protocol. Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later. The SASLPrep algorithm, for pre-processing the password, is not yet implemented. That could cause trouble, if you use a password with non-ASCII characters, and a client library that does implement SASLprep. That will hopefully be added later. Authorization identities, as specified in the SCRAM-SHA-256 specification, are ignored. SET SESSION AUTHORIZATION provides more or less the same functionality, anyway. If a user doesn't exist, perform a "mock" authentication, by constructing an authentic-looking challenge on the fly. The challenge is derived from a new system-wide random value, "mock authentication nonce", which is created at initdb, and stored in the control file. We go through these motions, in order to not give away the information on whether the user exists, to unauthenticated users. Bumps PG_CONTROL_VERSION, because of the new field in control file. Patch by Michael Paquier and Heikki Linnakangas, reviewed at different stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev, and many others. Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
$libpq->RemoveFile('src/common/sha2_openssl.c');
}
else
{
$libpq->RemoveFile('src/common/sha2.c');
}
my $libpqwalreceiver =
$solution->AddProject('libpqwalreceiver', 'dll', '',
'src/backend/replication/libpqwalreceiver');
$libpqwalreceiver->AddIncludeDir('src/interfaces/libpq');
$libpqwalreceiver->AddReference($postgres, $libpq);
2017-05-18 01:01:23 +02:00
my $pgoutput = $solution->AddProject('pgoutput', 'dll', '',
'src/backend/replication/pgoutput');
$pgoutput->AddReference($postgres);
my $pgtypes = $solution->AddProject(
'libpgtypes', 'dll',
'interfaces', 'src/interfaces/ecpg/pgtypeslib');
$pgtypes->AddDefine('FRONTEND');
$pgtypes->AddReference($libpgport);
$pgtypes->UseDef('src/interfaces/ecpg/pgtypeslib/pgtypeslib.def');
$pgtypes->AddIncludeDir('src/interfaces/ecpg/include');
my $libecpg = $solution->AddProject('libecpg', 'dll', 'interfaces',
'src/interfaces/ecpg/ecpglib');
$libecpg->AddDefine('FRONTEND');
$libecpg->AddIncludeDir('src/interfaces/ecpg/include');
$libecpg->AddIncludeDir('src/interfaces/libpq');
$libecpg->AddIncludeDir('src/port');
$libecpg->UseDef('src/interfaces/ecpg/ecpglib/ecpglib.def');
$libecpg->AddLibrary('ws2_32.lib');
$libecpg->AddReference($libpq, $pgtypes, $libpgport);
my $libecpgcompat = $solution->AddProject(
'libecpg_compat', 'dll',
'interfaces', 'src/interfaces/ecpg/compatlib');
$libecpgcompat->AddDefine('FRONTEND');
$libecpgcompat->AddIncludeDir('src/interfaces/ecpg/include');
$libecpgcompat->AddIncludeDir('src/interfaces/libpq');
$libecpgcompat->UseDef('src/interfaces/ecpg/compatlib/compatlib.def');
$libecpgcompat->AddReference($pgtypes, $libecpg, $libpgport);
my $ecpg = $solution->AddProject('ecpg', 'exe', 'interfaces',
'src/interfaces/ecpg/preproc');
$ecpg->AddIncludeDir('src/interfaces/ecpg/include');
$ecpg->AddIncludeDir('src/interfaces/libpq');
$ecpg->AddPrefixInclude('src/interfaces/ecpg/preproc');
$ecpg->AddFiles('src/interfaces/ecpg/preproc', 'pgc.l', 'preproc.y');
$ecpg->AddDefine('ECPG_COMPILE');
$ecpg->AddReference($libpgcommon, $libpgport);
my $pgregress_ecpg =
$solution->AddProject('pg_regress_ecpg', 'exe', 'misc');
$pgregress_ecpg->AddFile('src/interfaces/ecpg/test/pg_regress_ecpg.c');
$pgregress_ecpg->AddFile('src/test/regress/pg_regress.c');
$pgregress_ecpg->AddIncludeDir('src/port');
$pgregress_ecpg->AddIncludeDir('src/test/regress');
$pgregress_ecpg->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
$pgregress_ecpg->AddLibrary('ws2_32.lib');
$pgregress_ecpg->AddDirResourceFile('src/interfaces/ecpg/test');
$pgregress_ecpg->AddReference($libpgcommon, $libpgport);
my $isolation_tester =
$solution->AddProject('isolationtester', 'exe', 'misc');
$isolation_tester->AddFile('src/test/isolation/isolationtester.c');
$isolation_tester->AddFile('src/test/isolation/specparse.y');
$isolation_tester->AddFile('src/test/isolation/specscanner.l');
$isolation_tester->AddFile('src/test/isolation/specparse.c');
$isolation_tester->AddIncludeDir('src/test/isolation');
$isolation_tester->AddIncludeDir('src/port');
$isolation_tester->AddIncludeDir('src/test/regress');
$isolation_tester->AddIncludeDir('src/interfaces/libpq');
$isolation_tester->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
$isolation_tester->AddLibrary('ws2_32.lib');
$isolation_tester->AddDirResourceFile('src/test/isolation');
$isolation_tester->AddReference($libpq, $libpgcommon, $libpgport);
my $pgregress_isolation =
$solution->AddProject('pg_isolation_regress', 'exe', 'misc');
$pgregress_isolation->AddFile('src/test/isolation/isolation_main.c');
$pgregress_isolation->AddFile('src/test/regress/pg_regress.c');
$pgregress_isolation->AddIncludeDir('src/port');
$pgregress_isolation->AddIncludeDir('src/test/regress');
$pgregress_isolation->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
$pgregress_isolation->AddLibrary('ws2_32.lib');
$pgregress_isolation->AddDirResourceFile('src/test/isolation');
$pgregress_isolation->AddReference($libpgcommon, $libpgport);
# src/bin
my $D;
opendir($D, 'src/bin') || croak "Could not opendir on src/bin!\n";
while (my $d = readdir($D))
{
next if ($d =~ /^\./);
next unless (-f "src/bin/$d/Makefile");
next if (grep { /^$d$/ } @frontend_excludes);
AddSimpleFrontend($d);
}
my $pgbasebackup = AddSimpleFrontend('pg_basebackup', 1);
$pgbasebackup->AddFile('src/bin/pg_basebackup/pg_basebackup.c');
$pgbasebackup->AddLibrary('ws2_32.lib');
my $pgreceivewal = AddSimpleFrontend('pg_basebackup', 1);
$pgreceivewal->{name} = 'pg_receivewal';
$pgreceivewal->AddFile('src/bin/pg_basebackup/pg_receivewal.c');
$pgreceivewal->AddLibrary('ws2_32.lib');
my $pgrecvlogical = AddSimpleFrontend('pg_basebackup', 1);
$pgrecvlogical->{name} = 'pg_recvlogical';
$pgrecvlogical->AddFile('src/bin/pg_basebackup/pg_recvlogical.c');
$pgrecvlogical->AddLibrary('ws2_32.lib');
my $pgrewind = AddSimpleFrontend('pg_rewind', 1);
$pgrewind->{name} = 'pg_rewind';
$pgrewind->AddFile('src/backend/access/transam/xlogreader.c');
$pgrewind->AddLibrary('ws2_32.lib');
$pgrewind->AddDefine('FRONTEND');
my $pgevent = $solution->AddProject('pgevent', 'dll', 'bin');
$pgevent->AddFiles('src/bin/pgevent', 'pgevent.c', 'pgmsgevent.rc');
$pgevent->AddResourceFile('src/bin/pgevent', 'Eventlog message formatter',
'win32');
$pgevent->RemoveFile('src/bin/pgevent/win32ver.rc');
$pgevent->UseDef('src/bin/pgevent/pgevent.def');
$pgevent->DisableLinkerWarnings('4104');
my $pgdump = AddSimpleFrontend('pg_dump', 1);
$pgdump->AddIncludeDir('src/backend');
$pgdump->AddFile('src/bin/pg_dump/pg_dump.c');
$pgdump->AddFile('src/bin/pg_dump/common.c');
$pgdump->AddFile('src/bin/pg_dump/pg_dump_sort.c');
$pgdump->AddLibrary('ws2_32.lib');
my $pgdumpall = AddSimpleFrontend('pg_dump', 1);
# pg_dumpall doesn't use the files in the Makefile's $(OBJS), unlike
# pg_dump and pg_restore.
# So remove their sources from the object, keeping the other setup that
# AddSimpleFrontend() has done.
my @nodumpall = grep { m!src/bin/pg_dump/.*\.c$! }
keys %{ $pgdumpall->{files} };
delete @{ $pgdumpall->{files} }{@nodumpall};
$pgdumpall->{name} = 'pg_dumpall';
$pgdumpall->AddIncludeDir('src/backend');
$pgdumpall->AddFile('src/bin/pg_dump/pg_dumpall.c');
$pgdumpall->AddFile('src/bin/pg_dump/dumputils.c');
$pgdumpall->AddLibrary('ws2_32.lib');
my $pgrestore = AddSimpleFrontend('pg_dump', 1);
$pgrestore->{name} = 'pg_restore';
$pgrestore->AddIncludeDir('src/backend');
$pgrestore->AddFile('src/bin/pg_dump/pg_restore.c');
$pgrestore->AddLibrary('ws2_32.lib');
my $zic = $solution->AddProject('zic', 'exe', 'utils');
$zic->AddFiles('src/timezone', 'zic.c');
$zic->AddDirResourceFile('src/timezone');
$zic->AddReference($libpgcommon, $libpgport);
if (!$solution->{options}->{xml})
{
push @contrib_excludes, 'xml2';
}
if (!$solution->{options}->{openssl})
{
push @contrib_excludes, 'sslinfo';
}
if (!$solution->{options}->{uuid})
{
push @contrib_excludes, 'uuid-ossp';
}
# AddProject() does not recognize the constructs used to populate OBJS in
# the pgcrypto Makefile, so it will discover no files.
my $pgcrypto =
$solution->AddProject('pgcrypto', 'dll', 'crypto', 'contrib/pgcrypto');
$pgcrypto->AddFiles(
'contrib/pgcrypto', 'pgcrypto.c',
'px.c', 'px-hmac.c',
'px-crypt.c', 'crypt-gensalt.c',
'crypt-blowfish.c', 'crypt-des.c',
'crypt-md5.c', 'mbuf.c',
'pgp.c', 'pgp-armor.c',
'pgp-cfb.c', 'pgp-compress.c',
'pgp-decrypt.c', 'pgp-encrypt.c',
'pgp-info.c', 'pgp-mpi.c',
'pgp-pubdec.c', 'pgp-pubenc.c',
'pgp-pubkey.c', 'pgp-s2k.c',
'pgp-pgsql.c');
if ($solution->{options}->{openssl})
{
$pgcrypto->AddFiles('contrib/pgcrypto', 'openssl.c',
'pgp-mpi-openssl.c');
}
else
{
$pgcrypto->AddFiles(
'contrib/pgcrypto', 'md5.c',
'sha1.c', 'internal.c',
'internal-sha2.c', 'blf.c',
'rijndael.c', 'pgp-mpi-internal.c',
'imath.c');
}
$pgcrypto->AddReference($postgres);
$pgcrypto->AddReference($libpgcommon);
$pgcrypto->AddLibrary('ws2_32.lib');
my $mf = Project::read_file('contrib/pgcrypto/Makefile');
GenerateContribSqlFiles('pgcrypto', $mf);
foreach my $subdir ('contrib', 'src/test/modules')
{
opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
while (my $d = readdir($D))
{
next if ($d =~ /^\./);
next unless (-f "$subdir/$d/Makefile");
next if (grep { /^$d$/ } @contrib_excludes);
AddContrib($subdir, $d);
}
closedir($D);
}
# Build Perl and Python modules after contrib/ modules to satisfy some
# dependencies with transform contrib modules, like hstore_plpython
# ltree_plpython and hstore_plperl.
if ($solution->{options}->{python})
{
2015-05-24 03:35:49 +02:00
# Attempt to get python version and location.
# Assume python.exe in specified dir.
2015-05-24 03:35:49 +02:00
my $pythonprog = "import sys;print(sys.prefix);"
. "print(str(sys.version_info[0])+str(sys.version_info[1]))";
my $prefixcmd =
$solution->{options}->{python} . "\\python -c \"$pythonprog\"";
my $pyout = `$prefixcmd`;
die "Could not query for python version!\n" if $?;
2015-05-24 03:35:49 +02:00
my ($pyprefix, $pyver) = split(/\r?\n/, $pyout);
# Sometimes (always?) if python is not present, the execution
# appears to work, but gives no data...
die "Failed to query python for version information\n"
if (!(defined($pyprefix) && defined($pyver)));
my $pymajorver = substr($pyver, 0, 1);
my $plpython = $solution->AddProject('plpython' . $pymajorver,
'dll', 'PLs', 'src/pl/plpython');
$plpython->AddIncludeDir($pyprefix . '/include');
$plpython->AddLibrary($pyprefix . "/Libs/python$pyver.lib");
$plpython->AddReference($postgres);
# Add transform modules dependent on plpython
my $hstore_plpython = AddTransformModule(
2015-05-24 03:35:49 +02:00
'hstore_plpython' . $pymajorver, 'contrib/hstore_plpython',
'plpython' . $pymajorver, 'src/pl/plpython',
'hstore', 'contrib/hstore');
2017-05-18 01:01:23 +02:00
$hstore_plpython->AddDefine(
'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"');
my $ltree_plpython = AddTransformModule(
2015-05-24 03:35:49 +02:00
'ltree_plpython' . $pymajorver, 'contrib/ltree_plpython',
'plpython' . $pymajorver, 'src/pl/plpython',
'ltree', 'contrib/ltree');
2017-05-18 01:01:23 +02:00
$ltree_plpython->AddDefine(
'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"');
}
if ($solution->{options}->{perl})
{
my $plperlsrc = "src/pl/plperl/";
my $plperl =
$solution->AddProject('plperl', 'dll', 'PLs', 'src/pl/plperl');
$plperl->AddIncludeDir($solution->{options}->{perl} . '/lib/CORE');
$plperl->AddDefine('PLPERL_HAVE_UID_GID');
foreach my $xs ('SPI.xs', 'Util.xs')
{
(my $xsc = $xs) =~ s/\.xs/.c/;
if (Solution::IsNewer("$plperlsrc$xsc", "$plperlsrc$xs"))
{
my $xsubppdir = first { -e "$_/ExtUtils/xsubpp" } @INC;
print "Building $plperlsrc$xsc...\n";
system( $solution->{options}->{perl}
. '/bin/perl '
. "$xsubppdir/ExtUtils/xsubpp -typemap "
. $solution->{options}->{perl}
. '/lib/ExtUtils/typemap '
. "$plperlsrc$xs "
. ">$plperlsrc$xsc");
if ((!(-f "$plperlsrc$xsc")) || -z "$plperlsrc$xsc")
{
unlink("$plperlsrc$xsc"); # if zero size
die "Failed to create $xsc.\n";
}
}
}
if (Solution::IsNewer(
'src/pl/plperl/perlchunks.h',
'src/pl/plperl/plc_perlboot.pl')
|| Solution::IsNewer(
'src/pl/plperl/perlchunks.h',
'src/pl/plperl/plc_trusted.pl'))
{
print 'Building src/pl/plperl/perlchunks.h ...' . "\n";
my $basedir = getcwd;
chdir 'src/pl/plperl';
system( $solution->{options}->{perl}
. '/bin/perl '
. 'text2macro.pl '
. '--strip="^(\#.*|\s*)$$" '
. 'plc_perlboot.pl plc_trusted.pl '
. '>perlchunks.h');
chdir $basedir;
if ((!(-f 'src/pl/plperl/perlchunks.h'))
|| -z 'src/pl/plperl/perlchunks.h')
{
unlink('src/pl/plperl/perlchunks.h'); # if zero size
die 'Failed to create perlchunks.h' . "\n";
}
}
if (Solution::IsNewer(
'src/pl/plperl/plperl_opmask.h',
'src/pl/plperl/plperl_opmask.pl'))
{
print 'Building src/pl/plperl/plperl_opmask.h ...' . "\n";
my $basedir = getcwd;
chdir 'src/pl/plperl';
system( $solution->{options}->{perl}
. '/bin/perl '
. 'plperl_opmask.pl '
. 'plperl_opmask.h');
chdir $basedir;
if ((!(-f 'src/pl/plperl/plperl_opmask.h'))
|| -z 'src/pl/plperl/plperl_opmask.h')
{
unlink('src/pl/plperl/plperl_opmask.h'); # if zero size
die 'Failed to create plperl_opmask.h' . "\n";
}
}
$plperl->AddReference($postgres);
my $perl_path = $solution->{options}->{perl} . '\lib\CORE\perl*.lib';
my @perl_libs =
2017-05-18 01:01:23 +02:00
grep { /perl\d+.lib$/ } glob($perl_path);
if (@perl_libs == 1)
{
$plperl->AddLibrary($perl_libs[0]);
}
else
{
2017-05-18 01:01:23 +02:00
die
"could not identify perl library version matching pattern $perl_path\n";
}
# Add transform module dependent on plperl
2015-05-24 03:35:49 +02:00
my $hstore_plperl = AddTransformModule(
'hstore_plperl', 'contrib/hstore_plperl',
'plperl', 'src/pl/plperl',
'hstore', 'contrib/hstore');
$hstore_plperl->AddDefine('PLPERL_HAVE_UID_GID');
}
$mf =
Project::read_file('src/backend/utils/mb/conversion_procs/Makefile');
$mf =~ s{\\\r?\n}{}g;
$mf =~ m{SUBDIRS\s*=\s*(.*)$}m
|| die 'Could not match in conversion makefile' . "\n";
foreach my $sub (split /\s+/, $1)
{
my $dir = 'src/backend/utils/mb/conversion_procs/' . $sub;
my $p = $solution->AddProject($sub, 'dll', 'conversion procs', $dir);
$p->AddFile("$dir/$sub.c"); # implicit source file
$p->AddReference($postgres);
}
$mf = Project::read_file('src/bin/scripts/Makefile');
$mf =~ s{\\\r?\n}{}g;
$mf =~ m{PROGRAMS\s*=\s*(.*)$}m
|| die 'Could not match in bin/scripts/Makefile' . "\n";
foreach my $prg (split /\s+/, $1)
{
my $proj = $solution->AddProject($prg, 'exe', 'bin');
$mf =~ m{$prg\s*:\s*(.*)$}m
|| die 'Could not find script define for $prg' . "\n";
my @files = split /\s+/, $1;
foreach my $f (@files)
{
$f =~ s/\.o$/\.c/;
if ($f =~ /\.c$/)
{
$proj->AddFile('src/bin/scripts/' . $f);
}
}
$proj->AddIncludeDir('src/interfaces/libpq');
$proj->AddReference($libpq, $libpgfeutils, $libpgcommon, $libpgport);
$proj->AddDirResourceFile('src/bin/scripts');
$proj->AddLibrary('ws2_32.lib');
}
# Regression DLL and EXE
my $regress = $solution->AddProject('regress', 'dll', 'misc');
$regress->AddFile('src/test/regress/regress.c');
$regress->AddDirResourceFile('src/test/regress');
$regress->AddReference($postgres);
my $pgregress = $solution->AddProject('pg_regress', 'exe', 'misc');
$pgregress->AddFile('src/test/regress/pg_regress.c');
$pgregress->AddFile('src/test/regress/pg_regress_main.c');
$pgregress->AddIncludeDir('src/port');
$pgregress->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
$pgregress->AddLibrary('ws2_32.lib');
$pgregress->AddDirResourceFile('src/test/regress');
$pgregress->AddReference($libpgcommon, $libpgport);
# fix up pg_waldump once it's been set up
# files symlinked on Unix are copied on windows
my $pg_waldump = AddSimpleFrontend('pg_waldump');
$pg_waldump->AddDefine('FRONTEND');
foreach my $xf (glob('src/backend/access/rmgrdesc/*desc.c'))
{
$pg_waldump->AddFile($xf);
}
$pg_waldump->AddFile('src/backend/access/transam/xlogreader.c');
$solution->Save();
return $solution->{vcver};
}
#####################
# Utility functions #
#####################
# Add a simple frontend project (exe)
sub AddSimpleFrontend
{
my $n = shift;
my $uselibpq = shift;
my $p = $solution->AddProject($n, 'exe', 'bin');
$p->AddDir('src/bin/' . $n);
$p->AddReference($libpgfeutils, $libpgcommon, $libpgport);
if ($uselibpq)
{
$p->AddIncludeDir('src/interfaces/libpq');
$p->AddReference($libpq);
}
2015-04-09 14:15:39 +02:00
# Adjust module definition using frontend variables
AdjustFrontendProj($p);
return $p;
}
# Add a simple transform module
sub AddTransformModule
{
2015-05-24 03:35:49 +02:00
my $n = shift;
my $n_src = shift;
my $pl_proj_name = shift;
my $pl_src = shift;
my $transform_name = shift;
2015-05-24 03:35:49 +02:00
my $transform_src = shift;
my $transform_proj = undef;
foreach my $proj (@{ $solution->{projects}->{'contrib'} })
{
if ($proj->{name} eq $transform_name)
{
$transform_proj = $proj;
last;
}
}
die "could not find base module $transform_name for transform module $n"
2015-05-24 03:35:49 +02:00
if (!defined($transform_proj));
my $pl_proj = undef;
foreach my $proj (@{ $solution->{projects}->{'PLs'} })
{
if ($proj->{name} eq $pl_proj_name)
{
$pl_proj = $proj;
last;
}
}
die "could not find PL $pl_proj_name for transform module $n"
2015-05-24 03:35:49 +02:00
if (!defined($pl_proj));
my $p = $solution->AddProject($n, 'dll', 'contrib', $n_src);
for my $file (glob("$n_src/*.c"))
{
$p->AddFile($file);
}
$p->AddReference($postgres);
# Add PL dependencies
$p->AddIncludeDir($pl_src);
$p->AddReference($pl_proj);
$p->AddIncludeDir($pl_proj->{includes});
2015-05-24 03:35:49 +02:00
foreach my $pl_lib (@{ $pl_proj->{libraries} })
{
$p->AddLibrary($pl_lib);
}
# Add base module dependencies
$p->AddIncludeDir($transform_src);
$p->AddIncludeDir($transform_proj->{includes});
2015-05-24 03:35:49 +02:00
foreach my $trans_lib (@{ $transform_proj->{libraries} })
{
$p->AddLibrary($trans_lib);
}
$p->AddReference($transform_proj);
return $p;
}
# Add a simple contrib project
sub AddContrib
{
my $subdir = shift;
2015-05-24 03:35:49 +02:00
my $n = shift;
my $mf = Project::read_file("$subdir/$n/Makefile");
if ($mf =~ /^MODULE_big\s*=\s*(.*)$/mg)
{
my $dn = $1;
2015-05-24 03:35:49 +02:00
my $proj = $solution->AddProject($dn, 'dll', 'contrib', "$subdir/$n");
$proj->AddReference($postgres);
AdjustContribProj($proj);
}
elsif ($mf =~ /^MODULES\s*=\s*(.*)$/mg)
{
foreach my $mod (split /\s+/, $1)
{
my $proj =
$solution->AddProject($mod, 'dll', 'contrib', "$subdir/$n");
my $filename = $mod . '.c';
$proj->AddFile("$subdir/$n/$filename");
$proj->AddReference($postgres);
AdjustContribProj($proj);
}
}
elsif ($mf =~ /^PROGRAM\s*=\s*(.*)$/mg)
{
2015-05-24 03:35:49 +02:00
my $proj = $solution->AddProject($1, 'exe', 'contrib', "$subdir/$n");
AdjustContribProj($proj);
}
else
{
croak "Could not determine contrib module type for $n\n";
}
# Are there any output data files to build?
GenerateContribSqlFiles($n, $mf);
}
sub GenerateContribSqlFiles
{
my $n = shift;
my $mf = shift;
$mf =~ s{\\\r?\n}{}g;
if ($mf =~ /^DATA_built\s*=\s*(.*)$/mg)
{
my $l = $1;
# Strip out $(addsuffix) rules
if (index($l, '$(addsuffix ') >= 0)
{
my $pcount = 0;
my $i;
for ($i = index($l, '$(addsuffix ') + 12; $i < length($l); $i++)
{
$pcount++ if (substr($l, $i, 1) eq '(');
$pcount-- if (substr($l, $i, 1) eq ')');
last if ($pcount < 0);
}
$l =
substr($l, 0, index($l, '$(addsuffix ')) . substr($l, $i + 1);
}
foreach my $d (split /\s+/, $l)
{
my $in = "$d.in";
my $out = "$d";
if (Solution::IsNewer("contrib/$n/$out", "contrib/$n/$in"))
{
print "Building $out from $in (contrib/$n)...\n";
my $cont = Project::read_file("contrib/$n/$in");
my $dn = $out;
2015-05-24 03:35:49 +02:00
$dn =~ s/\.sql$//;
$cont =~ s/MODULE_PATHNAME/\$libdir\/$dn/g;
my $o;
open($o, '>', "contrib/$n/$out")
|| croak "Could not write to contrib/$n/$d";
print $o $cont;
close($o);
}
}
}
}
sub AdjustContribProj
{
my $proj = shift;
AdjustModule(
$proj, $contrib_defines,
\@contrib_uselibpq, \@contrib_uselibpgport,
\@contrib_uselibpgcommon, $contrib_extralibs,
$contrib_extrasource, $contrib_extraincludes);
}
sub AdjustFrontendProj
{
my $proj = shift;
2015-05-24 03:35:49 +02:00
AdjustModule(
$proj, $frontend_defines,
\@frontend_uselibpq, \@frontend_uselibpgport,
\@frontend_uselibpgcommon, $frontend_extralibs,
$frontend_extrasource, $frontend_extraincludes);
}
sub AdjustModule
{
my $proj = shift;
my $module_defines = shift;
my $module_uselibpq = shift;
my $module_uselibpgport = shift;
my $module_uselibpgcommon = shift;
my $module_extralibs = shift;
my $module_extrasource = shift;
my $module_extraincludes = shift;
my $n = $proj->{name};
if ($module_defines->{$n})
{
foreach my $d ($module_defines->{$n})
{
$proj->AddDefine($d);
}
}
if (grep { /^$n$/ } @{$module_uselibpq})
{
$proj->AddIncludeDir('src\interfaces\libpq');
$proj->AddReference($libpq);
}
if (grep { /^$n$/ } @{$module_uselibpgport})
{
$proj->AddReference($libpgport);
}
if (grep { /^$n$/ } @{$module_uselibpgcommon})
{
$proj->AddReference($libpgcommon);
}
if ($module_extralibs->{$n})
{
foreach my $l (@{ $module_extralibs->{$n} })
{
$proj->AddLibrary($l);
}
}
if ($module_extraincludes->{$n})
{
foreach my $i (@{ $module_extraincludes->{$n} })
{
$proj->AddIncludeDir($i);
}
}
if ($module_extrasource->{$n})
{
foreach my $i (@{ $module_extrasource->{$n} })
{
print "Files $i\n";
$proj->AddFile($i);
}
}
}
1;