OpenDDS Post Release 3.31.0
[OpenDDS.git] / configure
blob10eff06cd7258548c2403bc166500353997d1c78
1 #! /usr/bin/perl
2 # -*- CPerl -*-
3 eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
4 & eval 'exec perl -S $0 $argv:q'
5 if 0;
7 # configure script for OpenDDS
8 # Distributed under the OpenDDS License.
9 # See: http://www.opendds.org/license.html
11 use strict;
12 use warnings;
14 use Getopt::Long;
15 use Dumpvalue;
16 use File::Spec;
17 use File::Basename;
18 use File::Copy;
19 use File::Path;
20 use File::Temp ();
21 use FileHandle;
22 use Cwd;
23 use POSIX qw(strftime);
24 use B qw/perlstring/;
25 use Digest::MD5;
27 use FindBin;
28 use lib "$FindBin::RealBin/tools/scripts/modules";
29 use command_utils;
30 use ChangeDir;
31 use ini qw/read_ini_file/;
33 my $backup_timestamp = strftime "%Y-%m-%d-%H-%M-%S", localtime time;
35 # save args before Getopt modifies them
36 my @ARGS = @ARGV;
38 my @default_configh = (
39 '#define ACE_DISABLE_MKTEMP',
40 '#define ACE_DISABLE_READDIR_R',
41 '#define ACE_DISABLE_TEMPNAM',
42 '#define TAO_HAS_UIOP 0',
45 sub perlOS_to_host {
46 return 'win32' if $^O eq 'MSWin32';
47 return 'macos' if $^O eq 'darwin';
48 return $^O;
51 sub perlOS_to_java_platform {
52 return 'win32' if $^O eq 'MSWin32';
53 return $^O;
56 my $targetUsageIndent = "\t\t";
58 my %platforminfo =
59 ('win32' => {
60 'compilers' => ['cl'],
61 'libpath' => 'PATH',
62 'cl_versions' => {13.1 => 'vc71', 14 => 'vc8', 15 => 'vc9',
63 16 => 'vc10', 17 => 'vc11', 18 => 'vc12',
64 19 => 'vc14', 19.1 => 'vs2017',
65 19.2 => 'vs2019', 19.3 => 'vs2022', 19.4 => 'vs2022'},
66 'cl_archs' => {'x64' => 1, 'Win32' => 1, 'ARM64' => 0, 'ARM' => 0}, # 1 if supported
68 'macos' => {
69 'compilers' => ['clang++'],
70 'libpath' => 'DYLD_LIBRARY_PATH',
71 'aceconfig' => 'macosx',
72 'aceplatform' => 'macosx',
74 'macos-cross' => {
75 'compilers' => ['clang++'],
76 'libpath' => 'DYLD_LIBRARY_PATH',
77 'aceconfig' => 'macosx',
78 'aceplatform' => 'macosx',
79 'no_host' => 1,
80 'java_platform' => 'darwin',
81 'usage' => ['Use --target-arch or --target-compiler to ' .
82 'specify how the target', 'build should work'],
84 'linux' => {
85 'compilers' => ['g++', 'clang++', 'clang'],
86 'libpath' => 'LD_LIBRARY_PATH',
87 'aceplatform' => 'linux_$NONSTDCOMP', # $NONSTDCOMP = clang
89 'linux-cross' => {
90 'libpath' => 'LD_LIBRARY_PATH',
91 'aceplatform' => 'linux',
92 'aceconfig' => 'linux',
93 'no_host' => 1,
94 'java_platform' => 'linux',
95 'usage' => ['Use --target-compiler to specify the ' .
96 'cross-compiler binary',
99 'freebsd' => {
100 'compilers' => ['clang++'],
101 'libpath' => 'LD_LIBRARY_PATH',
103 'lynxos-178' => {
104 'libpath' => 'LD_LIBRARY_PATH',
105 'no_host' => 1,
106 'aceplatform' => 'lynxos',
107 'compiler_root_env' => 'ENV_PREFIX',
108 'usage' => ['Set up the cross compile using '.
109 'the script from LynxOS',
112 'vxworks' => {
113 'libpath' => 'LD_LIBRARY_PATH',
114 'no_host' => 1,
115 'usage' => ['Use the wrenv script before running configure',
116 'Specify the VSB with --macros=VSB_DIR=<dir>',
119 'android' => {
120 'libpath' => 'LD_LIBRARY_PATH',
121 'aceplatform' => 'android',
122 'aceconfig' => 'android',
123 'no_host' => 1,
124 'usage' => [
125 "Use --macros=ANDROID_ABI=<ARCH> to specify the",
126 "target architecture.",
127 "Use --macros=android_sdk=<SDK_PATH> and",
128 "--macros=android_target_api=<API_NUMBER> to specify",
129 "where to find android.jar.",
131 'needs_i2jrt_corba' => 1,
132 'java_platform' => 'android',
134 'ios' => {
135 'compilers' => ['clang++'],
136 'libpath' => 'DYLD_LIBRARY_PATH',
137 'no_host' => 1,
138 'aceconfig' => 'macosx-iOS',
139 'aceplatform' => 'macosx_iOS',
140 'usage' => [
141 "Use --macros=IPHONE_TARGET=SIMULATOR or",
142 "--macros=IPHONE_TARGET=HARDWARE",
143 "to specify the target architecture."
148 sub targetUsage {
149 my $status = shift;
150 print "Cross-compile targets: specify --target=TGT where TGT is one of:\n";
151 for my $k (sort keys %platforminfo) {
152 if ($platforminfo{$k}->{'no_host'}) {
153 print "\t$k\n";
154 if ($platforminfo{$k}->{'usage'}) {
155 for my $line (@{$platforminfo{$k}->{'usage'}}) {
156 print "${targetUsageIndent}$line\n";
161 exit $status;
164 ## arg processing and usage
166 my $argPadding = 29;
167 my $argIndent = "\n " . (' ' x $argPadding);
169 # Array of array-refs, each inner array is an option group which
170 # has the format [Group Description, Opt1 Spec, Opt1 Description,
171 # Opt2 Spec, Opt2 Description, ...]
172 # <default>OPT is a custom token marking a string option that
173 # defaults to $opts{'OPT'} = '' and can be overridden by opts by passing
174 # --no-OPT or --OPT with a value.
175 # <hidden>OPT hides the option from --help
176 # <usage>OPT hides the option from GetOpt::Long
177 my @specs =
179 ['Options controlling the configure script:',
180 'help|h|?', 'Show this help and exit',
181 'target-help', 'Show details of cross-compile target configs',
182 'verbose|v', 'Trace script execution',
183 'dry-run|n', 'Don\'t do anything',
184 'backup!', 'Make backup of build configuration files (yes)',
185 '<hidden>force-clone-submodules', 'Get submodules as if this wasn\'t a git repo',
187 ['Build platform and compiler:',
188 'host=s', 'Host (auto detect: linux, win32, macosx)',
189 'compiler=s', 'Compiler (auto detect / guess by searching PATH)',
190 'std=s', 'C++ standard version (compiler default)',
191 'target=s', 'Cross-compile target (none): see --target-help',
192 'target-arch=s', 'Architecture for target (none): see --target-help',
193 'target-compiler=s', 'Compiler for target (if req\'d): see --target-help',
194 'host-tools=s', 'DDS_ROOT of host tools for cross compile (build)',
195 'host-ace=s', 'Define host ACE_ROOT (uses relative path from' .
196 $argIndent . 'target DDS_ROOT to target ACE_ROOT)',
197 'host-tools-only!', 'Just build the host tools (no)',
198 'prefix=s', 'Installation prefix (none)',
199 'install-origin-relative!', 'Install with RPATH relative to $ORIGIN (no)',
200 'workspace=s', 'Custom MPC workspace file to copy and use' .
201 $argIndent . '(Use a builtin one)',
203 ['Build flags:',
204 'debug!', 'Debugging (yes)',
205 'optimize!', 'Optimization (no)',
206 'inline!', 'Inlining (yes)',
207 'static!', 'Static libraries (no)',
208 'ipv6!', 'IPv6 support (no)',
209 'sanitize=s@', 'Build with a sanitizer, can pass multiple times' .
210 $argIndent . 'or one list separated by commas, combining asan' .
211 $argIndent . "and tsan isn't recommended (no sanitizers)" .
212 $argIndent . ' asan: Address Sanitizer, gcc/clang only' .
213 $argIndent . ' tsan: Thread Sanitizer, gcc/clang only' .
214 $argIndent . ' ubsan: Undefined Behavior Sanitizer, clang only',
215 'compile-warnings=s', 'Enable additional compiler warnings' .
216 $argIndent . '(default compiler warnings)' .
217 $argIndent . ' WARNING: enable additional warnings' .
218 $argIndent . ' ERROR: enable additional warnings that are' .
219 $argIndent . ' treated as errors',
221 ['Required dependencies for OpenDDS:',
222 'ace=s', 'ACE (use ACE_ROOT, ACE_wrappers, or download)',
223 'tao=s', 'TAO (use TAO_ROOT, ACE_ROOT/TAO, or download)',
224 'mpc=s', 'MPC (use MPC_ROOT, ACE_ROOT/MPC, or download)',
225 'doc-group|doc_group!', 'Use the DOC Group release of TAO 2.5.x (yes)',
226 'doc-group3|doc_group3!', 'Use the DOC Group release of TAO 3.x (no)',
227 'ace-tao=s', 'Use the ACE/TAO version from acetao.ini',
228 'ace-github-latest!', 'Clone latest ACE/TAO/MPC from GitHub (no)',
229 'force-ace-tao', 'Force configuration of ACE/TAO (no)',
230 'no-disable-deprecated', 'Turn off disabling deprecated interfaces when' .
231 $argIndent . 'configuring ACE/TAO (no)',
233 ['Advanced configuration:',
234 'configh=s@', 'Extra text for config.h',
235 'macros=s@', 'Extra text for platform_macros.GNU',
236 'features=s@', 'Extra text for default.features',
237 'mpcopts=s@', 'Extra command-line options for MPC' .
238 $argIndent . 'This option can be given multiple times' .
239 $argIndent . 'For example:' .
240 $argIndent . ' --mpcopts=-value_template --mpcopts=build_flags+="-Wall -Werror"' .
241 $argIndent . 'turns into the following arguments for MPC:' .
242 $argIndent . ' -value_template build_flags+="-Wall -Werror"',
243 '<usage>mpc:OPT=VALUE', 'Extra command-line options for MPC' .
244 $argIndent . 'For example:' .
245 $argIndent . ' --mpc:value_template build_flags+="-Wall -Werror"' .
246 $argIndent . 'turns into the following arguments for MPC:' .
247 $argIndent . ' -value_template build_flags+="-Wall -Werror"' .
248 $argIndent . 'This option can be given multiple times',
249 'boottime!', 'Use CLOCK_BOOTTIME for timers (no)',
251 ['Optional dependencies for OpenDDS (disabled by default unless noted otherwise):',
252 'java:s', 'Java development kit (use JAVA_HOME)',
253 'jboss:s', 'JBoss application server (use JBOSS_HOME)',
254 'ant:s', 'Ant for JBoss (use ANT_HOME or system pkg)',
255 'wireshark:s',
256 'Wireshark dev headers or source not built with' .
257 $argIndent . 'CMake (use WIRESHARK_SRC or system pkg)' .
258 $argIndent . 'Implies --glib',
259 'wireshark-cmake|wireshark_cmake:s',
260 'Wireshark source built with CMake, requires' .
261 $argIndent . '--wireshark-build. Requires --wireshark-lib if' .
262 $argIndent . 'guessing fails (use WIRESHARK_SRC)' .
263 $argIndent . 'Implies --glib',
264 'wireshark-build|wireshark_build=s', 'Wireshark CMake Build Location',
265 'wireshark-lib|wireshark_lib=s',
266 'Optional Wireshark CMake libraries location' .
267 $argIndent . 'relative to wireshark-build (guesses)',
268 'glib:s', 'GLib for Wireshark (use GLIB_ROOT or system pkg)',
269 '<default>rapidjson:s',
270 'RapidJSON for Wireshark dissector and JSON' .
271 $argIndent . 'Sample Serialization (Enabled by default,' .
272 $argIndent . 'use git submodule, RAPIDJSON_ROOT, or system pkg)',
273 'qt:s', 'Qt5 (use QTDIR or system pkg)',
274 'qt-include:s', 'Qt include dir (use QT5_INCDIR, QTDIR/include,' .
275 $argIndent . 'or system package)',
276 'xerces3:s', 'Xerces-C++ 3 for QoS XML handling, DDS Security',
277 'openssl:s', 'OpenSSL for DDS Security',
278 'cmake:s', 'Path to CMake for compiling GoogleTest' .
279 $argIndent . '(Check PATH and normal locations)',
280 'gtest:s', 'Path to GoogleTest, required for tests' .
281 $argIndent . '(uses GTEST_ROOT)' .
282 $argIndent . 'If not built, will try to build using CMake.',
284 ['Optional OpenDDS features:',
285 'built-in-topics!', 'Built-in Topics (yes)',
286 'content-subscription!', 'Content-Subscription Profile (yes)',
287 'content-filtered-topic!', 'ContentFilteredTopic (CS Profile) (yes)',
288 'multi-topic!', 'MultiTopic (CS Profile) (yes)',
289 'query-condition!', 'QueryCondition (CS Profile) (yes)',
290 'ownership-profile!', 'Ownership Profile (yes)',
291 'ownership-kind-exclusive!', 'Exclusive Ownership (Ownership Profile) (yes)',
292 'object-model-profile!', 'Object Model Profile (yes)',
293 'persistence-profile!', 'Persistence Profile (yes)',
294 'safety-profile:s', 'Safety Profile: base or extended (none)',
295 'tests!', 'Build tests, examples, and performance tests (no)' .
296 $argIndent . 'Requires --gtest if missing git submodule',
297 'security!', 'DDS Security plugin (no) Implies --openssl and' .
298 $argIndent . '--xerces3',
302 sub iterate {
303 my $callback = shift;
304 for my $group (@specs) {
305 for my $n (1 .. (scalar @{$group} / 2)) {
306 my $opt = ${$group}[$n * 2 - 1];
307 my $descr = ${$group}[$n * 2];
308 $opt =~ /(\<\w+\>)?([\w-]+)/;
309 my $optkey = (defined $1 ? $1 : "") . $2;
310 &$callback(${$group}[0], $opt, $descr, $optkey, @_);
315 sub usage {
316 my $status = shift;
317 my $current;
318 my $ver;
319 open VER, 'dds/Version.h' or die "ERROR: can't open dds/Version.h, stopped";
320 while (<VER>) {
321 $ver = $1 if /#define OPENDDS_VERSION "([^"]+)"/;
323 close VER;
324 print <<"EOT";
325 Welcome to OpenDDS version $ver
327 Options for this script are listed below, with the default behavior described
328 in parenthesis after the option description.
329 Boolean options can take the form "--opt" or "--no-opt", the more commonly
330 needed one (the one that changes the default behavior) is shown below.
331 Options that require arguments are shown as "--opt=VAL". Options with optional
332 arguments are shown as "--opt[=VAL]". Options that can be repeated with
333 cumulative effect are shown with a trailing "...". Some third-party
334 optional dependencies can be automatically located if they are installed in the
335 expected locations (see entries below marked with "system pkg"). In those
336 cases, specify the option as --opt without an = to enable the corresponding
337 feature in OpenDDS and use the default installation location.
339 iterate(sub {
340 my ($group, $opt, $descr, $optkey) = @_;
341 if (!defined $current || $group ne $current) {
342 $current = $group;
343 print "\n$group\n";
345 $optkey .= '=VAL' if $opt =~ /=s/;
346 $optkey .= '[=VAL]' if $opt =~ /:s/;
347 $optkey = "[no-]$optkey" if ($opt =~ /!$/ && $descr =~ / \(yes\)/);
348 if ($optkey =~ /^\<hidden\>/) {
349 return;
351 if ($opt =~ /^\<usage\>(.*)$/) {
352 $optkey = $1;
354 $optkey =~ s/^\<default\>(.*)$/\[no-\]$1/g;
355 $optkey .= '...' if $opt =~ /s\@$/;
356 my $pad = $argPadding - length $optkey;
357 print "--$optkey" . ' ' x (($pad > 0) ? $pad : 0) . " $descr\n";
360 exit $status;
363 my $defaulted = {};
365 sub parseArgs {
366 my @getopts = ();
367 my @default = ();
368 iterate(sub {
369 my ($group, $opt, $descr, $optkey) = @_;
370 if ($opt =~ /^\<usage\>/) {
371 return;
373 if ($opt =~ /^<default>(.*)$/) {
374 push @getopts, $1;
375 $optkey =~ /^<default>(.*)$/;
376 push @getopts, "no-$1";
377 push @default, $1;
379 else {
380 $opt =~ s/^\<hidden\>(.*)$/$1/;
381 push @getopts, $opt;
386 if (! -r 'rules.dds.GNU') {
387 print "ERROR: this script must be run from its own directory\n";
388 exit 1;
391 my $opts = {};
392 Getopt::Long::Configure('pass_through');
393 GetOptions($opts, @getopts) or usage(1);
394 usage(0) if $opts->{'help'};
395 targetUsage(0) if $opts->{'target-help'};
397 while (@ARGV != 0) {
398 my $arg = shift(@ARGV);
399 if ($arg =~ /^--mpc:([^=]*)(?:=(.*))?$/) {
400 my $key = $1;
401 my $value = $2;
402 if (defined($value)) {
403 push(@{$opts->{'mpcopts'}}, '-' . $key, $value);
405 elsif (@ARGV != 0) {
406 $value = shift(@ARGV);
407 push(@{$opts->{'mpcopts'}}, '-' . $key, $value);
408 } else {
409 print STDERR "ERROR: $arg requires a value\n";
410 usage(1);
412 } else {
413 print STDERR "ERROR: unknown argument $arg\n";
414 usage(1);
418 if ($opts->{'verbose'}) {
419 print "Options:\n";
420 new Dumpvalue()->dumpValue($opts);
423 for my $opt (@default) {
424 if ($opt eq 'rapidjson' && defined $opts->{'safety-profile'}) {
425 print("Although it's a default, rapidjson is not compatible with Safety ".
426 "Profile so it will not be enabled\n") if $opts->{'verbose'};
427 next;
429 if (!exists $opts->{$opt} && !exists $opts->{"no-$opt"}) {
430 $opts->{$opt} = '';
431 $defaulted->{$opt} = 1;
432 print("By default, --$opt is added to the options\n") if $opts->{'verbose'};
436 return $opts;
439 my $cross_compile = 0;
440 my %opts = %{parseArgs()};
442 my $debug = exists $opts{'debug'} ? $opts{'debug'} : 1;
443 my $backup = exists $opts{'backup'} ? $opts{'backup'} : 1;
444 my $force_ace_tao = exists $opts{'force-ace-tao'} ? $opts{'force-ace-tao'} : 0;
445 my $no_disable_deprecated = exists $opts{'no-disable-deprecated'} ? $opts{'no-disable-deprecated'} : 0;
446 my $cxx_std;
447 if (exists($opts{'std'})) {
448 $cxx_std = $opts{'std'};
449 # Accept any of --std=17, --std=stdcpp17, --std=c++17, etc.
450 $cxx_std =~ s/^(std)?(cpp|c\+\+)//;
452 my @mpcopts;
453 push(@mpcopts, @{$opts{'mpcopts'}}) if exists($opts{'mpcopts'});
454 my @features;
455 push(@features, @{$opts{'features'}}) if exists($opts{'features'});
457 $opts{'host'} = perlOS_to_host() unless $opts{'host'};
459 my $is_windows = $opts{'host'} eq 'win32';
461 my ($slash, $exeext) = $is_windows ? ('\\', '.exe') : ('/', '');
463 my %specific =
464 ($is_windows ?
465 ('ext' => 'cmd', 'pathsep' => ';', 'refpre' => '%',
466 'refpost' => '%', 'comment' => '::') :
467 ('ext' => 'sh', 'pathsep' => ':', 'refpre' => '${',
468 'refpost' => '}', 'comment' => '#')
471 sub might_be_executable {
472 my $path = shift;
473 return -x $path && -f $path; # On Windows -x can return true for directories
476 sub which {
477 my $file = shift;
478 for my $p (File::Spec->path()) {
479 next if $p eq '.';
480 my $path = "$p/$file";
481 if (might_be_executable($path)) {
482 return $path;
484 elsif ($exeext ne '') {
485 $path .= $exeext;
486 if (might_be_executable($path)) {
487 return $path;
491 return undef;
494 sub addCurLibPathRef {
495 my $buildEnvRef = shift;
496 my $platform = $opts{$buildEnvRef->{'build'}};
497 my $libpathname = $platforminfo{$platform}->{'libpath'};
498 my $curLibPathRef = $specific{'refpre'} . $libpathname . $specific{'refpost'};
499 $buildEnvRef->{$libpathname} = $curLibPathRef;
502 sub run_command {
503 my $command = shift;
504 return command_utils::run_command(
505 $command,
506 script_name => 'configure',
507 dry_run => $opts{'dry-run'},
508 verbose => $opts{'verbose'},
513 sub git_clone {
514 my $dest = shift;
515 my $url = shift;
516 my %args = @_;
517 my $branch = $args{branch};
518 my $commit = $args{commit};
520 print "Cloning git repo $url ", $branch // $commit, "\n";
522 my $failed;
523 if (defined($commit)) {
524 # Git can't directly clone a specific commit. Could use clone and checkout,
525 # but can't do a single shallow clone that way.
526 my $chdir = ChangeDir->new($dest);
527 $failed = run_command(['git', 'init', '--quiet']) ||
528 run_command(['git', 'remote', 'add', 'origin', $url]) ||
529 run_command(['git', 'fetch', '--quiet', '--depth=1', 'origin', $commit]) ||
530 run_command(['git', 'checkout', '--quiet', 'FETCH_HEAD']);
532 else {
533 my @cmd = ('git', 'clone', '--quiet', '--depth=1', $url, $dest);
534 push(@cmd, '--branch', $branch) if (defined($branch));
535 $failed = run_command(\@cmd);
538 if (!$failed) {
539 run_command(['git', '--no-pager', 'log', '-1', '--oneline'], chdir => $dest);
541 return $failed;
544 sub git_submodule_prop {
545 my $path = shift;
546 my $prop_name = shift;
547 my $full_prop_name = "submodule.$path.$prop_name";
548 open(my $fd, "-|", "git config --file .gitmodules --get $full_prop_name")
549 or die("git_submodule_prop open failed: $!\nStopped");
550 my $prop_value = <$fd>;
551 close($fd);
552 chomp($prop_value) if (defined($prop_value));
553 if (!$prop_value) {
554 die("Couldn't get $full_prop_name from .gitmodules\nStopped");
556 return $prop_value;
559 sub git_ensure_submodule {
560 my $path = shift;
562 if (!which('git')) {
563 print STDERR "Can't ensure we have $path becuase we can't find the git command\n";
564 return 0;
567 my $commit_prop = 'openddsConfigureCommit';
568 my $commit = git_submodule_prop($path, $commit_prop);
570 if (!$opts{'force-clone-submodules'} &&
571 !run_command(['git', 'submodule', 'update', '--init', $path])) {
572 # This doesn't affect us in this run, but since we can get the actual
573 # submodule commit, make sure it matches the commit from .gitmodules for
574 # the case when the source tree is not a git repo.
575 open(my $fd, "-|", "git ls-tree HEAD $path")
576 or die("git_ensure_submodule open failed: $!\nStopped");
577 my $real_commit = <$fd>;
578 close($fd);
579 chomp($real_commit);
580 $real_commit =~ s/^\S+ \S+ ([0-9a-f]+).*$/$1/;
581 if ($real_commit ne $commit) {
582 print STDERR "WARNING: for submodule $path $commit_prop = $commit ",
583 "from .gitmodules doesn't match actual commit $real_commit\n";
586 return 1;
589 return !git_clone($path, git_submodule_prop($path, 'url'), commit => $commit);
592 sub md5sum {
593 my $path = shift();
594 my $expected_hash = shift();
596 my $md5 = Digest::MD5->new;
597 open(my $fh, $path) or die("Couldn't open \"$path\": $!");
598 binmode $fh;
599 $md5->addfile($fh);
600 my $hash = $md5->hexdigest();
601 my $failed = $expected_hash ne $hash;
602 if ($failed) {
603 print(
604 "MD5 hash mismatch for $path\n",
605 " expected: $expected_hash\n",
606 " got: $hash\n");
608 return $failed;
611 my $curpathRef = $specific{'refpre'} . 'PATH' . $specific{'refpost'};
612 my %hostEnv = ('build' => 'host', 'PATH' => $curpathRef);
613 my %targetEnv = ('build' => 'target', 'PATH' => $curpathRef);
615 my $would_download; # can dry-run assume directories that don't exist yet?
617 sub locate_mpc {
618 my $ace_src = shift;
619 if (defined $opts{'mpc'}) {
620 setEnv('MPC_ROOT', $opts{'mpc'});
622 elsif (!$ENV{'MPC_ROOT'} && (-r $ace_src . '/MPC/MPC.ico'
623 || ($opts{'dry-run'} && $would_download))) {
624 setEnv('MPC_ROOT', $ace_src . $slash . 'MPC');
626 elsif (!$ENV{'MPC_ROOT'}) {
627 die "ERROR: Can't find MPC. Please set MPC_ROOT or make sure MPC exists".
628 "\n in the 'MPC' directory under ACE_ROOT ($ace_src), stopped";
630 else {
631 $targetEnv{'MPC_ROOT'} = $ENV{'MPC_ROOT'};
633 $hostEnv{'MPC_ROOT'} = $targetEnv{'MPC_ROOT'};
636 if (!exists $platforminfo{$opts{'host'}} ||
637 $platforminfo{$opts{'host'}}->{'no_host'}) {
638 die "ERROR: unknown host $opts{'host'}, stopped";
640 print "host system is: $opts{'host'}\n" if $opts{'verbose'};
642 $opts{'target'} = $opts{'host'} unless $opts{'target'};
644 if (!exists $platforminfo{$opts{'target'}}) {
645 die "ERROR: unknown target $opts{'target'}, stopped";
648 if (exists($opts{'workspace'})) {
649 if (!-r $opts{'workspace'}) {
650 die "ERROR: workspace file $opts{'workspace'} isn't a readable file, stopped";
652 $opts{'workspace'} = Cwd::realpath($opts{'workspace'});
655 if (exists($opts{'prefix'}) && !File::Spec->file_name_is_absolute($opts{'prefix'})) {
656 die("ERROR: --prefix argument $opts{'prefix'} is not an absolute path, stopped");
659 # Set initial libpath
660 addCurLibPathRef(\%hostEnv);
661 addCurLibPathRef(\%targetEnv);
664 if (defined $opts{'safety-profile'} && $opts{'safety-profile'} eq '') {
665 print "Defaulting safety profile to extended\n";
666 $opts{'safety-profile'} = 'extended';
669 my $build_host_tools = 0;
670 if ($opts{'host'} ne $opts{'target'} || $opts{'safety-profile'}) {
671 $build_host_tools = !$opts{'host-tools'};
672 print "Cross-compile build " .
673 ($build_host_tools ? 'including' : 'excluding') . " host tools\n"
674 if $opts{'verbose'};
675 $cross_compile = 1;
677 my $has_host_compiler = $build_host_tools || !$cross_compile;
679 if ($platforminfo{$opts{'target'}}->{'compiler_root_env'}) {
680 # This target puts its cross-compiler in the PATH before the host compiler,
681 # we will need to override (not append to) PATH for the host build.
682 my $root_env = $platforminfo{$opts{'target'}}->{'compiler_root_env'};
683 my $root_dir = $ENV{$root_env};
684 my @oldpath = split /$specific{'pathsep'}/, $ENV{'PATH'};
685 my $newpath = join($specific{'pathsep'}, grep {!/^$root_dir/} @oldpath);
686 $hostEnv{'PATH'} =~
687 s/\Q$specific{'refpre'}\EPATH\Q$specific{'refpost'}\E/$newpath/;
690 if (!exists $platforminfo{$opts{'target'}}->{'needs_i2jrt_corba'}) {
691 $platforminfo{$opts{'target'}}->{'needs_i2jrt_corba'} = 0;
694 ## compiler
696 if ($has_host_compiler) {
697 if ($opts{'compiler'}) {
698 my $standard = 0;
699 for my $stdcomp (@{$platforminfo{$opts{'host'}}->{'compilers'}}) {
700 $standard = 1 if $opts{'compiler'} eq $stdcomp;
702 $opts{'nonstdcompiler'} = 1 unless $standard;
704 else {
705 print "Auto-detecting compiler\n" if $opts{'verbose'};
706 for my $stdcomp (@{$platforminfo{$opts{'host'}}->{'compilers'}}) {
707 my $path = which($stdcomp);
708 if ($path) {
709 print "Found $stdcomp at: $path\n" if $opts{'verbose'};
710 $opts{'compiler'} = $stdcomp;
711 last;
714 if (!defined $opts{'compiler'}) {
715 die "ERROR: Can't find a compiler, set PATH or run this script with the ".
716 "--compiler option.\n" . ($is_windows ? " For Microsoft Visual C++, ".
717 "run this script from the Visual Studio ".
718 "Command Prompt.\n" : '') . "Stopped";
721 print "compiler is: $opts{'compiler'}\n" if $opts{'verbose'};
723 if ($opts{'compiler'} =~ /cl(\.exe)?$/i) {
724 my $savepath = $ENV{'PATH'};
725 my $clpath = which($opts{'compiler'});
726 if ($clpath) {
727 $clpath =~ s/vc\\bin(\\(x86_)?amd64)?/common7\\ide/i;
728 $ENV{'PATH'} .= ";$clpath";
731 # Have CL Tell Us Its Target Architecture and Version
732 my ($tmp_fd, $tmp_filename) = File::Temp::tempfile();
733 my $cl_check = << "EOF";
734 #ifdef _M_X64
735 # define ARCH x64
736 #elif defined(_M_IX86)
737 # define ARCH Win32
738 #elif defined(_M_ARM64)
739 # define ARCH ARM64
740 #elif defined(_M_ARM)
741 # define ARCH ARM
742 #else
743 # define ARCH Unknown
744 #endif
745 CL is _MSC_VER ARCH
747 print $tmp_fd $cl_check;
748 close($tmp_fd);
749 my $cl_command = "\"$opts{'compiler'}\" /EP $tmp_filename 2>&1";
750 print "Running $cl_command\n" if $opts{'verbose'};
751 open(my $cl_out_fd, "-|", $cl_command)
752 or die "ERROR: Could not detect Visual C++ version, try running this ".
753 "script from the Visual Studio Command Prompt.\nStopped";
754 my $ver = 0;
755 my $arch = '';
756 while (my $line = <$cl_out_fd>) {
757 chomp ($line);
758 print "CL says: $line\n" if $opts{'verbose'} && $line !~ /^\s*$/;
759 # Convert _MSC_VER to the form \d+\.\d
760 if ($line =~ /^CL is (\d+)\d (\w+)$/) {
761 $ver = int($1) / 10;
762 $arch = $2;
763 last;
766 close($cl_out_fd);
767 unlink($tmp_filename)
768 or warn "Unable to delete temporary file $tmp_filename: $!";
769 print "CL Version is $ver, architecture is $arch\n" if $opts{'verbose'};
770 if (!$ver && !$arch) {
771 die "cl version probe failed, invalid output from cl\nStopped";
773 if ($arch eq 'Unknown') {
774 die "cl version probe failed, no known architecture macro was defined\nStopped";
777 my $archs = $platforminfo{'win32'}->{'cl_archs'};
778 if (!exists($archs->{$arch})) {
779 die "cl version probe failed, unknown architecture \"$arch\"\nStopped";
781 if (!$archs->{$arch}) {
782 die "ERROR: Windows for $arch isn't supported\nStopped";
784 $opts{'compiler_target_architecture'} = $arch;
786 my $vers = $platforminfo{'win32'}->{'cl_versions'};
787 if (!exists($vers->{$ver})) {
788 my @vers_sorted = sort(keys(%{$vers}));
789 my $last_cl = $vers_sorted[-1];
790 print "$ver isn't a known version of cl, using the last known version: $last_cl\n"
791 if $opts{'verbose'};
792 $ver = $last_cl;
794 $opts{'compiler_version'} = $vers->{$ver};
796 if ($ver >= 19) {
797 push(@features, 'no_cxx11=0');
798 print "Visual C++ has >= C++11 support\n" if $opts{'verbose'};
800 if ($opts{'std'}) {
801 my $std = "stdcpp$cxx_std";
802 push(@mpcopts, '-value_template', "LanguageStandard=$std");
803 print "Setting Visual C++ LanguageStandard to $std\n" if $opts{'verbose'};
804 if ($opts{'std'} eq 'latest' || $cxx_std >= 17) {
805 push(@features, 'no_cxx17=0');
806 print "Visual C++ has >= C++17 support\n" if $opts{'verbose'};
809 $ENV{'PATH'} = $savepath;
810 print "Detected Visual C++ version: $opts{'compiler_version'}\n"
811 if $opts{'verbose'};
813 elsif ($opts{'compiler'} =~ /g\+\+|clang/) {
814 my $version_string = `$opts{'compiler'} --version`;
815 print "Compiler version: $version_string\n" if $opts{'verbose'};
816 $opts{'is apple clang'} = $version_string =~ /^Apple/;
817 if ($opts{'std'}) {
818 push(@{$opts{'macros'}}, 'CCFLAGS += -std=' . $opts{'std'});
819 print "Added platform_macros for -std=$opts{std}\n" if $opts{'verbose'};
821 elsif ($opts{'compiler'} !~ /clang/) {
822 $version_string =~ /\(.*\) (\d+)\.\d+/;
823 if ($1 >= 11) {
824 $opts{'std'} = 'gnu++17';
825 print "Detected GCC >= 11, default -std=gnu++17\n" if $opts{'verbose'};
827 elsif ($1 >= 6) {
828 $opts{'std'} = 'gnu++14';
829 print "Detected GCC >= 6, default -std=gnu++14\n" if $opts{'verbose'};
832 elsif ($opts{'compiler'} =~ /clang/ && $version_string !~ /^Apple / &&
833 $version_string =~ /(\d+)\.(\d+)\.(\d+)/) {
834 # Apple's version of Clang doesn't default to c++11 or higher, users
835 # can pass --std= to change the C++ standard version used.
836 if ($1 >= 16) {
837 $opts{'std'} = 'gnu++17';
838 print "Detected Clang >= 16, default -std=gnu++17\n" if $opts{'verbose'};
840 elsif ($1 >= 6) {
841 # Non-Apple versions of Clang, if version 6 or newer, default to C++14 like GCC
842 $opts{'std'} = 'gnu++14';
843 print "Detected Clang >= 6, default -std=gnu++14\n" if $opts{'verbose'};
847 if ($opts{'std'}) {
848 my $cpp17 = qr/(17|2a|20|2b|23)/;
849 if ($opts{'std'} =~ /(0x|11|1y|14|1z|$cpp17)$/) {
850 push(@features, 'no_cxx11=0');
851 print "Compiler has >= C++11 support\n" if $opts{'verbose'};
854 if ($opts{'std'} =~ /$cpp17$/) {
855 push(@features, 'no_cxx17=0');
856 print "Compiler has >= C++17 support\n" if $opts{'verbose'};
862 my $compile_warnings = $opts{'compile-warnings'} // '';
863 if ($compile_warnings eq 'WARNING' or $compile_warnings eq 'ERROR') {
864 my ($section_names, $sections) = read_ini_file("$FindBin::RealBin/build.ini");
865 my %compiler_map = ( 'g++' => 'GNU', 'clang' => 'Clang', 'cl' => 'MSVC', 'cl.exe' => 'MSVC');
866 my $section = $compiler_map{$opts{'compiler'}};
867 if ($opts{'is apple clang'}) {
868 $section = 'AppleClang';
870 my $warning_flags = $sections->{$section}{'warning'};
871 my $error_flags = $sections->{$section}{'error'};
872 if ($section eq 'MSVC') {
873 $warning_flags =~ s/^\/W//;
874 push(@mpcopts, '-value_template', "warning_level+=${warning_flags}");
875 if ($compile_warnings eq 'ERROR') {
876 push(@mpcopts, '-value_template', 'WarnAsError=1');
878 } else {
879 push(@mpcopts, '-value_template', "compile_flags+=${warning_flags}");
880 if ($compile_warnings eq 'ERROR') {
881 push(@mpcopts, '-value_template', "compile_flags+=${error_flags}");
886 sub looksRelative {
887 my $val = shift;
888 return substr($val, 0, 1) ne $slash && ($slash eq '/' || $val !~ /^[a-z]:/i);
891 sub normalizePath {
892 my $val = shift;
893 return Cwd::abs_path($val) if $val && -d $val && $val =~ /../;
894 return $val;
897 sub setSomeEnv {
898 my($hashref, $name, $val, $notdir) = @_;
899 $val = Cwd::abs_path($val) if -d $val;
900 if ($opts{'dry-run'} && !$notdir && looksRelative($val)) {
901 $val = getcwd . $slash . $val;
903 $val =~ s!/!\\!g if $is_windows;
904 $hashref->{$name} = $val;
907 sub setEnv {
908 setSomeEnv(\%targetEnv, @_);
911 sub setHostEnv {
912 setSomeEnv(\%hostEnv, @_);
915 ## ace
916 my $ace_src;
918 if ($opts{'ace'}) {
919 if ($opts{'ace'} ne 'download') {
920 if (!-r $opts{'ace'} . '/ace/ACE.h') {
921 die "ERROR: Can't find ACE at $opts{'ace'}.\nStopped";
923 $ace_src = $opts{'ace'};
926 elsif ($ENV{'ACE_ROOT'}) {
927 if (!-r $ENV{'ACE_ROOT'} . '/ace/ACE.h') {
928 die "ERROR: Can't find ACE at $ENV{'ACE_ROOT'}.\nStopped";
930 $ace_src = $ENV{'ACE_ROOT'};
932 elsif (-r '../ACE_wrappers/ace/ACE.h') {
933 die "ERROR: Older versions of this script would default to using ACE at " .
934 "../ACE_wrappers, but this version doesn't. Use the --ace command line " .
935 "option to override this error. Use --ace=download to have this script " .
936 "download an ACE+TAO package and expand it to ACE_wrappers.\nStopped";
938 elsif (-r 'ACE_wrappers/ace/ACE.h') {
939 $ace_src = 'ACE_wrappers';
941 elsif (-r 'ATCD/ACE/ace/ACE.h') {
942 $ace_src = 'ATCD/ACE';
944 elsif (-r 'ACE_TAO/ACE/ace/ACE.h') {
945 $ace_src = 'ACE_TAO/ACE';
948 $ace_src = normalizePath($ace_src);
950 ## tao
951 my $tao_src;
953 if ($opts{'tao'}) {
954 if (!-r $opts{'tao'} . '/tao/ORB.h') {
955 die "ERROR: Can't find TAO at $opts{'tao'}.\nStopped";
957 $tao_src = $opts{'tao'};
959 elsif ($ENV{'TAO_ROOT'}) {
960 if (!-r $ENV{'TAO_ROOT'} . '/tao/ORB.h') {
961 die "ERROR: Can't find TAO at $ENV{'TAO_ROOT'}.\nStopped";
963 $tao_src = $ENV{'TAO_ROOT'};
965 elsif (defined $ace_src && -r $ace_src . '/TAO/tao/ORB.h') {
966 $tao_src = $ace_src . $slash . 'TAO';
968 elsif (defined $ace_src && -r $ace_src . '/../TAO/tao/ORB.h') {
969 $tao_src = (File::Spec->splitpath($ace_src))[1] .'TAO';
972 $tao_src = normalizePath($tao_src);
974 if ($opts{'safety-profile'}) {
975 # convert to lower case
976 $opts{'safety-profile'} = lc($opts{'safety-profile'});
979 ## Download ACE+TAO
980 if (!$ace_src || !$tao_src) {
981 if ($opts{'ace-github-latest'}) {
982 die "ERROR: Git not found in path (required to clone ACE/TAO/MPC)"
983 if ! which('git');
985 my $urlbase = 'https://github.com/DOCGroup';
986 my $branch = 'ace6tao2';
987 if ($opts{'doc-group3'}) {
988 $branch = 'master';
991 my $err = git_clone('ACE_TAO', "$urlbase/ACE_TAO", branch => $branch);
992 die "ERROR: Failed to clone ACE/TAO from GitHub\nStopped"
993 if $err ||
994 ! -r 'ACE_TAO/ACE/ace/ACE.h' ||
995 ! -r 'ACE_TAO/TAO/tao/ORB.h';
997 $err = git_clone('ACE_TAO/ACE/MPC', "$urlbase/MPC", branch => 'master');
998 die "ERROR: Failed to clone MPC (into ACE_TAO/ACE/MPC) from GitHub\nStopped"
999 if $err ||
1000 ! -r 'ACE_TAO/ACE/MPC/mwc.pl';
1002 $ace_src = normalizePath('ACE_TAO/ACE');
1003 $tao_src = normalizePath('ACE_TAO/TAO');
1005 else {
1006 # Get ACE/TAO version info
1007 my ($section_names, $sections) = read_ini_file("$FindBin::RealBin/acetao.ini");
1008 my $ace_tao_version = $opts{'doc-group3'} ? $sections->{ace7tao3} : $sections->{ace6tao2};
1009 if ($opts{'ace-tao'}) {
1010 $ace_tao_version = $sections->{$opts{'ace-tao'}};
1011 die "ERROR: No entry named '$opts{'ace-tao'}' in acetao.ini" unless $ace_tao_version;
1013 if ($opts{verbose}) {
1014 print("ACE/TAO Version Info:");
1015 new Dumpvalue()->dumpValue($ace_tao_version);
1017 my $ext = $is_windows ? 'zip' : 'tar.gz';
1018 my $file = $ace_tao_version->{"$ext-filename"};
1019 my $url = $ace_tao_version->{"$ext-url"};
1020 my $md5_hash = $ace_tao_version->{"$ext-md5"};
1022 # Check for an existing file
1023 if (-r $file) {
1024 if (md5sum($file, $md5_hash)) {
1025 if ($opts{'dry-run'}) {
1026 print("Would remove existing $file and attempt to download\n");
1028 else {
1029 print("Removing existing $file and attempting to download\n");
1030 unlink($file) or die("Couldn't remove $file: $!\nStopped");
1033 elsif ($opts{'verbose'}) {
1034 print("Using ACE+TAO source package $file\n");
1038 if (!-r $file) {
1039 my $dl_msg = "Downloading $file from $url using";
1040 $would_download = 1;
1041 eval {
1042 require LWP::UserAgent;
1043 my $ua = LWP::UserAgent->new;
1044 $ua->env_proxy;
1045 print("$dl_msg LWP\n");
1046 if ($opts{'dry-run'}) {
1047 print("Dry-run: would LWP::UserAgent get $url\n");
1049 else {
1050 my $response = $ua->get($url, ':content_file' => $file);
1051 if ($response->is_error) {
1052 die $response->message . "\nstopped";
1056 if ($@) {
1057 if (which('wget')) {
1058 print("$dl_msg wget\n");
1059 run_command(['wget', '--output-document', $file, $url], autodie => 1);
1061 elsif (which('curl')) {
1062 print("$dl_msg curl\n");
1063 run_command(['curl', '--location', $url, '--output', $file], autodie => 1);
1065 else {
1066 die "ERROR: Can't download ACE+TAO using LWP, wget, or curl.\n" .
1067 "Download ACE+TAO from $url, place the file here\n, " .
1068 "and re-run the script.\nStopped";
1073 if (!$opts{'dry-run'} && md5sum($file, $md5_hash)) {
1074 die("MD5 hash check failed after download, try running again?\nStopped");
1077 print "Extracting archive $file\n";
1078 $ENV{'ACTIVEPERL_CONFIG_DISABLE'} = 1 if $^O eq 'MSWin32';
1079 $ENV{'ACTIVEPERL_CONFIG_SILENT'} = 1 if $^O eq 'MSWin32';
1080 eval {require Archive::Extract;};
1081 if ($@) {
1082 my $err = 1;
1083 my $ddsroot = getcwd;
1084 if (!$is_windows) {
1085 $err = run_command(['tar', 'xzf', "$ddsroot/$file"]);
1087 else {
1088 # Try Archive::Zip
1089 print "Archive::Extract isn't installed, trying Archive::Zip\n" if $opts{'verbose'};
1090 eval {require Archive::Zip};
1091 if ($@) {
1092 print "Neither Archive::Extract or Archive::Zip are installed\n" if $opts{'verbose'};
1094 else {
1095 if ($opts{'dry-run'}) {
1096 print "Dry-run: would Archive::Zip $file\n";
1098 else {
1099 my $zip = Archive::Zip->new();
1100 if ($zip->read( $file ) == Archive::Zip::AZ_OK() &&
1101 $zip->extractTree() == Archive::Zip::AZ_OK()) {
1102 $err = 0;
1108 if ($err) {
1109 die "ERROR: Can't extract $file, extract it to " . Cwd::abs_path('.') .
1110 "\nand run this script again.\nStopped";
1113 else {
1114 if ($opts{'dry-run'}) {
1115 print "Dry-run: would Archive::Extract $file\n";
1117 else {
1118 if ($^O ne 'MSWin32') {
1119 no warnings 'once';
1120 $Archive::Extract::PREFER_BIN = 1;
1122 my $ae = Archive::Extract->new('archive' => $file);
1123 if (!$ae->extract('to' => '.')) {
1124 die $ae->error . "\nstopped";
1129 unlink $file;
1130 print "Removed $file\n" if $opts{'verbose'};
1131 $ace_src = 'ACE_wrappers';
1132 $tao_src = 'ACE_wrappers/TAO';
1136 print "Using ace_src: $ace_src\n" if $opts{'verbose'};
1137 print "Using tao_src: $tao_src\n" if $opts{'verbose'};
1139 sub clone_host_and_target {
1140 my $source_dir = shift;
1141 locate_mpc($ace_src);
1142 print "cloning build tree\n" if $opts{'verbose'};
1143 if (run_command(
1144 ["$targetEnv{'MPC_ROOT'}/clone_build_tree.pl", 'host', 'target'],
1145 chdir => $source_dir)) {
1146 die("Failed to clone tree");
1150 sub backup {
1151 my $file = shift;
1152 if (!$opts{'dry-run'} && -r $file) {
1153 print "WARNING: overwriting existing $file\n";
1154 if ($backup) {
1155 my $backup_path = $file . '.bak';
1156 $backup_path .= ".$backup_timestamp" if -e $backup_path;
1157 copy($file, $backup_path);
1158 print " (saved a backup copy as $backup_path)\n";
1160 unlink $file;
1164 sub backup_and_copy {
1165 my $src = shift;
1166 my $dst = shift;
1167 backup($dst);
1168 if ($opts{'dry-run'}) {
1169 print("Would copy $src to $dst\n");
1171 else {
1172 copy($src, $dst);
1176 sub backup_and_open {
1177 my $file = shift;
1178 backup($file);
1179 if ($opts{'dry-run'}) {
1180 return File::Temp->new();
1182 my $fh = new FileHandle;
1183 open $fh, ">$file" or die "ERROR: Can't write to $file, stopped";
1184 return $fh;
1187 sub dump_and_unlink { # removes temp files created by dry-run
1188 my $tfile = shift;
1189 if ($opts{'verbose'}) {
1190 open TMP, $tfile;
1191 print <TMP>;
1192 close TMP;
1194 unlink $tfile;
1197 sub write_config_h {
1198 my %buildEnv = %{shift()};
1199 my $platform = $opts{$buildEnv{'build'}};
1200 my $pi = $platforminfo{$platform};
1201 $opts{'optimize'} = 0 if (!exists $opts{'optimize'} && !exists $opts{'sanitize'});
1203 my $CFGH = backup_and_open("$buildEnv{'ACE_ROOT'}/ace/config.h");
1204 if (!$no_disable_deprecated) {
1205 for my $line (@default_configh) {
1206 print $CFGH "$line\n";
1209 if ($buildEnv{'build'} eq 'target') {
1210 for my $line (@{$opts{'configh'}}) {
1211 print $CFGH "$line\n";
1214 my $cfg = $platform;
1215 if ($pi->{'aceconfig'}) {
1216 $cfg = $pi->{'aceconfig'};
1217 $cfg =~ s/\$UNAMER/my $u = `uname -r`; chomp $u; $u/e;
1219 $cfg .= '-' . $opts{'host_version'} if $opts{'host_version'};
1220 print $CFGH "#include \"ace/config-$cfg.h\"\n";
1221 if (defined $opts{'no-opendds-safety-profile'}) {
1222 print $CFGH "#define ACE_FACE_SAFETY_" . uc($opts{'safety-profile'}) . "\n";
1223 if ($opts{'safety-profile'} eq 'extended') {
1224 print $CFGH "#ifndef ACE_HAS_ALLOC_HOOKS\n";
1225 print $CFGH "# define ACE_HAS_ALLOC_HOOKS\n";
1226 print $CFGH "#endif\n";
1230 close $CFGH;
1231 print "Wrote $buildEnv{'ACE_ROOT'}/ace/config.h\n" if $opts{'verbose'};
1232 dump_and_unlink($CFGH) if $opts{'dry-run'};
1235 my $wrote_df = 0;
1237 sub default_features {
1238 my %buildEnv = %{shift()};
1239 my @feat;
1240 if ($buildEnv{'build'} eq 'target') {
1241 push(@feat, 'ipv6=1') if $opts{'ipv6'};
1242 my @normalized = map {/=/ ? $_ : "$_=1"} @features;
1243 push(@feat, @normalized) if @normalized;
1244 } elsif ($opts{'java'}) {
1245 push(@feat, 'java=1');
1247 return @feat;
1250 sub write_default_features {
1251 my %buildEnv = %{shift()};
1252 my @feat = default_features(\%buildEnv);
1254 if (@feat) {
1255 my $DF = backup_and_open("$buildEnv{'ACE_ROOT'}/bin/MakeProjectCreator" .
1256 "/config/default.features");
1257 $wrote_df = 1;
1258 for my $f (@feat) {
1259 print $DF "$f\n";
1261 $DF->close;
1262 print "Wrote $buildEnv{'ACE_ROOT'}/.../default.features\n"
1263 if $opts{'verbose'};
1264 dump_and_unlink($DF) if $opts{'dry-run'};
1268 my %all_sanitizers = (
1269 asan => {
1270 fsanitize => 'address',
1271 env => {
1272 LSAN_OPTIONS => "suppressions=$FindBin::RealBin/etc/asan-suppr.txt",
1273 ASAN_OPTIONS => 'detect_leaks=1:fast_unwind_on_malloc=0:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1',
1275 compiler_args => ['-fsanitize-address-use-after-scope'],
1277 tsan => {
1278 fsanitize => 'thread',
1279 env => {
1280 TSAN_OPTIONS => "history_size=7 second_deadlock_stack=1 suppressions=$FindBin::RealBin/etc/tsan-suppr.txt",
1283 ubsan => {
1284 fsanitize => 'undefined',
1285 env => {
1286 UBSAN_OPTIONS => "suppressions=$FindBin::RealBin/etc/ubsan-suppr.txt:print_stacktrace=1",
1288 configh => ['#define ACE_INITIALIZE_MEMORY_BEFORE_USE'],
1289 compiler_args => ['-fno-sanitize=enum'],
1292 my %enabled_sanitizers = ();
1293 if (exists $opts{'sanitize'}) {
1294 for my $arg (@{$opts{'sanitize'}}) {
1295 for my $name (split(/,/, $arg)) {
1296 if (exists($all_sanitizers{$name})) {
1297 $enabled_sanitizers{$name} = $all_sanitizers{$name};
1299 else {
1300 die("ERROR: \"$name\" isn't a valid sanitizer to pass to --sanitize\nStopped");
1306 my @fsanitize = ();
1307 my @sanitizer_compiler_args = ();
1308 my @sanitizer_linker_args = ();
1309 for my $name (sort(keys(%enabled_sanitizers))) {
1310 my $sanitizer = $enabled_sanitizers{$name};
1311 push(@fsanitize, $sanitizer->{fsanitize});
1312 if (exists($sanitizer->{env})) {
1313 for my $env (keys(%{$sanitizer->{env}})) {
1314 setEnv($env, $sanitizer->{env}->{$env});
1317 if (exists($sanitizer->{configh})) {
1318 push(@{$opts{'configh'}}, @{$sanitizer->{configh}});
1320 if (exists($sanitizer->{compiler_args})) {
1321 push(@sanitizer_compiler_args, @{$sanitizer->{compiler_args}});
1323 if (exists($sanitizer->{linker_args})) {
1324 push(@sanitizer_linker_args, @{$sanitizer->{linker_args}});
1327 if (scalar(@fsanitize)) {
1328 if (!$debug) {
1329 die("ERROR: Using sanitizers requires --debug");
1331 my @common = ('-ggdb', '-fsanitize=' . join(',', @fsanitize));
1332 @sanitizer_compiler_args = ('-O1', @common, '-fno-omit-frame-pointer', @sanitizer_compiler_args);
1333 @sanitizer_linker_args = (@common, @sanitizer_linker_args);
1336 sub compiler {
1337 my %buildEnv = %{shift()};
1338 return ($buildEnv{'build'} eq 'target' && $opts{'target-compiler'}) ?
1339 $opts{'target-compiler'} : $opts{'compiler'};
1342 my @ace_macros = ('debug',
1343 'optimize',
1344 'inline',
1345 'static',
1346 'ipv6');
1348 my @platformmacros;
1349 sub write_platform_macros {
1350 my %buildEnv = %{shift()};
1351 if (!$is_windows ||
1352 ($cross_compile && $buildEnv{'build'} eq 'target')) {
1353 my $pi = $platforminfo{$opts{$buildEnv{'build'}}};
1354 my $PMG = backup_and_open("$buildEnv{'ACE_ROOT'}/include/makeinclude" .
1355 "/platform_macros.GNU");
1356 my $macro_cross_compile = 0;
1357 if ($buildEnv{'build'} eq 'target') {
1358 for my $line (@{$opts{'macros'}}) {
1359 print $PMG "$line\n";
1361 for my $key (@ace_macros) {
1362 if (exists $opts{$key}) {
1363 my $macro = ($key eq 'static') ? 'static_libs_only' : $key;
1364 print $PMG "$macro = $opts{$key}\n";
1367 if ($cross_compile) {
1368 print $PMG "CROSS-COMPILE = 1\n";
1369 print $PMG 'TAO_IDL = $(HOST_ACE)/bin/tao_idl', "\n";
1370 if ($is_windows) {
1371 print $PMG "HOST_EXE_EXT = .exe\n";
1373 print $PMG 'TAO_IDL_DEP = $(TAO_IDL)$(HOST_EXE_EXT)', "\n";
1374 if ($has_host_compiler) {
1375 print $PMG 'TAO_IDL_PREPROCESSOR = ', $opts{'compiler'}, "\n";
1377 print $PMG 'TAO_IDLFLAGS += -g $(HOST_ACE)/bin/ace_gperf', "\n";
1378 print $PMG 'build_tao_idl_be = 0', "\n";
1379 unless ($build_host_tools) {
1380 my $host_ace;
1381 if (exists $opts{'host-ace'}) {
1382 $host_ace = $opts{'host-ace'};
1384 else {
1385 $host_ace = '$(HOST_DDS)' . nested($ace_src, '.');
1387 print $PMG "HOST_ACE = $host_ace\n";
1389 if ($opts{'target-compiler'}) {
1390 my $tcomp = $opts{'target-compiler'};
1391 if ($tcomp =~ s/-g([c+])\1$/-/) {
1392 $macro_cross_compile = 1;
1393 print $PMG 'CROSS_COMPILE = ', $tcomp, "\n";
1395 else {
1396 $opts{'nonstdcompiler'} = $tcomp;
1397 print $PMG 'LDFLAGS += -Wl,-rpath-link,$(ACE_ROOT)/lib', "\n";
1400 if ($opts{'target-arch'}) {
1401 print $PMG "FLAGS_C_CC += -target $opts{'target-arch'}\n";
1402 print $PMG "LDFLAGS += -target $opts{'target-arch'}\n";
1404 if ($opts{'target'} eq 'android') {
1405 print $PMG 'ifeq (,$(findstring -isystem$(ACE_ROOT),$(INCLDIRS)))', "\n";
1406 print $PMG ' INCLDIRS += -isystem $(ACE_ROOT)', "\n";
1407 print $PMG 'endif', "\n";
1411 for my $f (@platformmacros) {
1412 print $PMG ($f =~ /=/ ? $f : "$f=1"), "\n";
1414 if ($buildEnv{'build'} eq 'host') {
1415 print $PMG "static_libs_only = 1\n";
1416 print $PMG "java = 1\n" if $opts{'java'};
1418 if ($opts{'prefix'}) {
1419 print $PMG "INSTALL_PREFIX=" . $opts{'prefix'} . "\n";
1421 if (scalar(@fsanitize)) {
1422 print $PMG
1423 'CPPFLAGS += ' . join(' ', @sanitizer_compiler_args) . "\n" .
1424 'LDFLAGS += ' . join(' ', @sanitizer_linker_args) . "\n";
1426 my $plat = $opts{$buildEnv{'build'}};
1427 if ($pi->{'aceplatform'}) {
1428 $plat = $pi->{'aceplatform'};
1429 $plat =~ s/\$COMP/'g++'/e;
1430 $plat =~ s/\$NONSTDCOMP/($opts{'compiler'} =~ m!clang!) ? 'clang' : ''/e;
1431 $plat =~ s/_$//;
1433 $plat .= '_' . $opts{'host_version'} if $opts{'host_version'};
1434 print $PMG "include \$(ACE_ROOT)/include/makeinclude/platform_$plat.GNU\n";
1435 if ($opts{'nonstdcompiler'} && !$macro_cross_compile) {
1436 my $comp = compiler(\%buildEnv);
1437 for my $var ('CC', 'CXX', 'LD') {
1438 print $PMG "$var = $comp\n";
1441 if ($opts{'prefix'} && $opts{'install-origin-relative'}) {
1442 print $PMG <<'EOT';
1443 install_rpath = 0
1444 INSTALL_ORIGIN = $$ORIGIN/../$(INSTALL_LIB)
1445 LDFLAGS += '-Wl,-rpath,$(INSTALL_ORIGIN)' $(LD_RPATH_FLAGS)
1448 $PMG->close;
1449 print "Wrote $buildEnv{'ACE_ROOT'}/.../platform_macros.GNU\n"
1450 if $opts{'verbose'};
1451 dump_and_unlink($PMG) if $opts{'dry-run'};
1455 sub write_opendds_configh {
1456 my %buildEnv = %{shift()};
1457 my $CFGH = backup_and_open("$buildEnv{'DDS_ROOT'}/dds/OpenDDSConfig.h");
1458 my $CFGIN = new FileHandle;
1459 open($CFGIN, 'dds/OpenDDSConfig.h.in') or die("Can't open OpenDDSConfig.h.in for reading: $!\nStopped");
1461 my %config = (
1462 'OPENDDS_CONFIG_AUTO_STATIC_INCLUDES' => 0,
1463 'OPENDDS_CONFIG_BOOTTIME_TIMERS' => $opts{'boottime'} // 0,
1464 'OPENDDS_CONFIG_SECURITY' => $opts{'security'} // 0,
1467 my $replace_value = sub {
1468 my $var = shift;
1469 return 'UNDEFINED_NEED_TO_UPDATE_CONFIGURE_SCRIPT' unless exists $config{$var};
1470 return $config{$var};
1473 while (<$CFGIN>) {
1474 s/@(\w+)@/&$replace_value($1)/e;
1475 print $CFGH $_;
1478 $CFGIN->close;
1479 $CFGH->close;
1480 print "Wrote $buildEnv{'DDS_ROOT'}/dds/OpenDDSConfig.h\n"
1481 if $opts{'verbose'};
1482 dump_and_unlink($CFGH) if $opts{'dry-run'};
1485 ## Optional OpenDDS dependencies
1486 my %optdep = (
1487 'java' => {env => 'JAVA_HOME', sanity => 'include/jni.h', mpc => 'java'},
1488 'jboss' => {env => 'JBOSS_HOME', sanity => 'lib/jboss-common.jar'},
1489 'ant' => {env => 'ANT_HOME', sanity => 'bin/ant'},
1490 'wireshark' => {env => 'WIRESHARK_SRC', sanity => 'epan/packet.h', mpc => 'wireshark'},
1491 'wireshark-cmake' => {
1492 env => 'WIRESHARK_SRC',
1493 sanity => 'epan/packet.h',
1494 mpc => 'wireshark_cmake',
1496 'wireshark-build' => {env => 'WIRESHARK_BUILD', sanity => 'config.h'},
1497 'wireshark-lib' => {env => 'WIRESHARK_LIB', may_be_blank => 1},
1498 'glib' => {
1499 env => 'GLIB_ROOT',
1500 sanity => {
1501 'include/glib-2.0/glib.h' => undef,
1502 'include/glib.h' => 'glib_versioned_includes=0',
1505 'rapidjson' => {
1506 env => 'RAPIDJSON_ROOT',
1507 sanity => 'include/rapidjson/rapidjson.h',
1508 mpc => 'no_rapidjson=0',
1510 'qt' => {env => 'QTDIR', sanity => '', mpc => 'qt5'},
1511 'xerces3' => {
1512 env => 'XERCESCROOT',
1513 sanity => 'include/xercesc/dom/DOM.hpp',
1514 mpc => 'xerces3',
1516 'openssl' => {env => 'SSL_ROOT', sanity => 'include/openssl/opensslv.h', mpc => 'ssl'},
1519 my $host_tools_only = exists $opts{'host-tools-only'} && $opts{'host-tools-only'};
1520 if ($host_tools_only) {
1521 print "--host-tools-only implies --static\n" if $opts{'verbose'};
1522 $opts{'static'} = 1;
1523 if ($cross_compile) {
1524 die "ERROR: Can't use --host-tools-only for cross compile\nStopped";
1528 if ((exists $opts{'wireshark'} || exists $opts{'wireshark-cmake'}) &&
1529 !exists $opts{'glib'}) {
1530 print "--wireshark and --wireshark-cmake imply --glib\n" if $opts{'verbose'};
1531 $opts{'glib'} = '';
1534 # Use this to check if tests are enabled (instead of $opts{'tests'} directly).
1535 my $tests = exists $opts{'tests'} && $opts{'tests'};
1537 my @ace_features = ('xerces3');
1539 if (exists $opts{'java'}) {
1540 if ($opts{'static'} && !$host_tools_only) {
1541 die "ERROR: --static can't be used with --java\nStopped";
1543 my $host_java_platform = perlOS_to_java_platform();
1544 if ($cross_compile) {
1545 if (exists $platforminfo{$opts{'target'}}->{'java_platform'}) {
1546 setEnv('JAVA_PLATFORM', $platforminfo{$opts{'target'}}->{'java_platform'});
1547 setHostEnv('JAVA_PLATFORM', $host_java_platform);
1549 else {
1550 die "Cross-compile with Java for $opts{'target'} in not supported.\nStopped";
1553 else {
1554 setEnv('JAVA_PLATFORM', $host_java_platform);
1558 my $try_to_use_qt_system_pkg =
1559 exists $opts{'qt'} && !length($opts{'qt'}) && !exists $opts{'qt-include'};
1561 # Default to Wireshark Development Package if installed and a path wasn't
1562 # supplied.
1563 my $wireshark_install = '/usr/include/wireshark';
1564 if (exists $opts{'wireshark'} && !defined $ENV{'WIRESHARK_SRC'} && $opts{'wireshark'} eq '') {
1565 my $sanity = $optdep{'wireshark'}->{sanity};
1566 if (-f File::Spec->catdir($wireshark_install, $sanity)) {
1567 $opts{'wireshark'} = $wireshark_install;
1569 else {
1570 die "ERROR: --wireshark must be given a value because there is not a " .
1571 "development package installed at " . $wireshark_install . ", stopped";
1575 my $wireshark_lib_defaulted = 0;
1576 if (exists $opts{'wireshark-cmake'} && !exists $opts{'wireshark-lib'}) {
1577 if ($^O =~ /MSWin32/) {
1578 $opts{'wireshark-lib'} = "run\\RelWithDebInfo";
1580 elsif ($^O =~ /darwin/) {
1581 $opts{'wireshark-lib'} = "run/Wireshark.app/Contents/Frameworks";
1583 elsif ($^O =~ /linux/) {
1584 $opts{'wireshark-lib'} = "";
1586 else {
1587 die "ERROR: --wireshark-lib is needed because we couldn't decide on a default value";
1589 $wireshark_lib_defaulted = 1;
1592 if ($opts{'security'}) {
1593 unless (exists $opts{'openssl'}) {
1594 $opts{'openssl'} = '';
1595 print "Forcing --openssl (security dependency)\n" if $opts{'verbose'};
1597 unless (exists $opts{'xerces3'}) {
1598 $opts{'xerces3'} = '';
1599 print "Forcing --xerces3 (security dependency)\n" if $opts{'verbose'};
1603 # Try to find CMake
1604 unless ($opts{'cmake'}) {
1605 print "--cmake not specified by user; searching path...\n" if $opts{'verbose'};
1606 $opts{'cmake'} = which('cmake');
1608 unless ($opts{'cmake'}) {
1609 print "CMake not found in path; searching default location...\n"
1610 if $opts{'verbose'};
1612 $opts{'cmake'} =
1613 ($is_windows ?
1614 (-f $ENV{'ProgramFiles'} . '\\CMake\\bin\\cmake.exe' ?
1615 $ENV{'ProgramFiles'} . '\\CMake\\bin\\cmake.exe' :
1616 $ENV{'ProgramFiles(x86)'} . '\\CMake\\bin\\cmake.exe') :
1617 '/usr/bin/cmake');
1620 my $has_cmake = -f $opts{'cmake'};
1621 if ($has_cmake) {
1622 print "Using CMake '$opts{'cmake'}' if needed\n" if $opts{'verbose'};
1624 else {
1625 print "Could not find CMake at '$opts{'cmake'}'\n" if $opts{'verbose'};
1628 sub targets_win64 {
1629 my $arch = exists $opts{'compiler_target_architecture'} ?
1630 $opts{'compiler_target_architecture'} : '';
1632 return $arch eq 'x64';
1635 sub system_default_install_dir {
1636 if ($is_windows) {
1637 # When the host is 32-bit.
1638 return $ENV{'ProgramFiles'}
1639 if ! exists $ENV{'ProgramFiles(x86)'};
1641 return targets_win64() ? $ENV{'ProgramFiles'} : $ENV{'ProgramFiles(x86)'};
1643 return '/usr';
1646 my $build_gtest = 0;
1647 if (exists $opts{'gtest'} || $tests) {
1648 my $gtest_sanity = 'include/gtest/gtest.h';
1649 my $gtest_root = undef;
1651 if ($opts{'gtest'}) {
1652 die "ERROR: '$gtest_sanity' not found in supplied gtest directory '$opts{'gtest'}'"
1653 if ! -f File::Spec->catfile($opts{'gtest'}, $gtest_sanity);
1655 $gtest_root = $opts{'gtest'};
1656 setEnv('GTEST_ROOT', $gtest_root);
1659 else {
1660 my $rel_path = 'tests/googletest';
1661 my $sm_dir = Cwd::abs_path($rel_path);
1662 my $sm_src = File::Spec->catdir($sm_dir, 'googletest');
1663 my $sys_dir = File::Spec->catdir((system_default_install_dir(),
1664 $is_windows ? 'googletest-distribution' : ''));
1666 my $use_sm_dir = 0;
1667 if (-f File::Spec->catfile($sm_src, $gtest_sanity)) {
1668 $use_sm_dir = 1;
1670 elsif (-f File::Spec->catfile($sys_dir, $gtest_sanity)) {
1671 $opts{'gtest'} = $sys_dir;
1672 $gtest_root = $sys_dir;
1673 setEnv('GTEST_ROOT', $gtest_root);
1675 else {
1676 print "Could not find GoogleTest, cloning...\n" if $opts{'verbose'};
1677 if (git_ensure_submodule($rel_path)) {
1678 $use_sm_dir = 1;
1682 if ($use_sm_dir) {
1683 # Check for existing build
1684 $gtest_root = File::Spec->catdir($sm_dir, 'build', 'install');
1685 $build_gtest = ! -d $gtest_root;
1686 $opts{'gtest'} = $sm_dir;
1687 # No need to set env. variable with submodule UNLESS we're in safetyprofile
1688 if ($opts{'safety-profile'}) {
1689 print "Setting GTEST_ROOT to ${gtest_root} for use in safety-profile.\n" if $opts{'verbose'};
1690 setEnv('GTEST_ROOT', $gtest_root);
1694 if (!defined($gtest_root)) {
1695 die "ERROR: GoogleTest '$gtest_sanity' not found in submodule src '$sm_src' or " .
1696 "default install dir '$sys_dir', please pass a correct version of GoogleTest to --gtest\nStopped";
1699 if ($is_windows && -d File::Spec->catdir($gtest_root, 'bin')) {
1700 push_path(\%targetEnv, $gtest_root . $slash . 'bin');
1702 elsif (!$is_windows && -f File::Spec->catfile($gtest_root, 'lib', 'libgtest.so')) {
1703 push_libpath(\%targetEnv, $gtest_root . $slash . 'lib');
1707 if ($build_gtest && !$has_cmake) {
1708 die "ERROR: GoogleTest in $opts{'gtest'} must be built but can't find CMake\nStopped";
1711 if (exists $opts{'rapidjson'}) {
1712 if ($opts{'rapidjson'} eq '') {
1713 my $sanity = $optdep{'rapidjson'}->{sanity};
1714 my $rel_path = 'tools/rapidjson';
1715 my $sm_dir = Cwd::abs_path($rel_path);
1716 my $sys_dir = File::Spec->catdir((system_default_install_dir(),
1717 $is_windows ? 'RapidJSON' : ''));
1719 my $rapidjson_msg =
1720 "Could not find RapidJSON (using '$sanity') in:\n".
1721 " Git Submodule: '$sm_dir'\n" .
1722 " Default Install Prefix: '$sys_dir'\n";
1723 my $rapidjson = undef;
1724 if (-f File::Spec->catfile($sm_dir, $sanity)) {
1725 $rapidjson = $sm_dir;
1727 elsif (-f File::Spec->catfile($sys_dir, $sanity)) {
1728 $rapidjson = $sys_dir;
1730 else {
1731 print "Could not find RapidJSON, cloning...\n" if $opts{'verbose'};
1732 if (git_ensure_submodule($rel_path)) {
1733 $rapidjson = $sm_dir;
1737 if (defined($rapidjson)) {
1738 print "Using '$rapidjson' for RapidJSON\n" if ($opts{'verbose'});
1739 $opts{'rapidjson'} = $rapidjson;
1741 elsif (exists($defaulted->{'rapidjson'})) {
1742 print "${rapidjson_msg}Continuing without it.\n" if $opts{'verbose'};
1743 delete $opts{'rapidjson'};
1745 else {
1746 die "ERROR: ${rapidjson_msg}Stopped";
1751 my %use_system_pkg = map {$_, 1} qw/ant glib qt xerces3 openssl/;
1753 my %use_win_default = (
1754 'openssl' => system_default_install_dir() . '/OpenSSL',
1755 'xerces3' => system_default_install_dir() . '/xerces-c');
1757 my %need_platform_macros = map {$_, 1} qw/xerces3/;
1759 sub default_java_home {
1760 my $host = $opts{'host'};
1761 my $result = '';
1762 if ($host eq 'macosx') {
1763 $result = `/usr/libexec/java_home`;
1764 chomp $result;
1765 return $result;
1767 else {
1768 $result = which('javac');
1769 if (!$result) {
1770 die "ERROR: No javac in PATH.\nStopped";
1772 while (-l $result) {
1773 $result = readlink($result);
1775 # remove bin/javac
1776 return dirname(dirname($result))
1780 # Detect and populate dependency info from environment variables
1781 # or system-wide default paths.
1782 for my $key (keys %optdep) {
1783 if (exists $opts{$key} && $opts{$key} eq '') {
1784 if (defined $optdep{$key}->{env} && $ENV{$optdep{$key}->{env}}) {
1785 $opts{$key} = $ENV{$optdep{$key}->{env}};
1787 if ($key eq 'java') {
1788 ## when the environment variable JAVA_HOME is set to a JRE location,
1789 ## try to resolve JAVA_HOME based on the location of javac
1790 my $java_home = default_java_home();
1791 if (!-r "$opts{$key}$slash$optdep{'java'}->{sanity}" and $java_home ne '') {
1792 $opts{'java'} = $java_home;
1796 elsif (!$is_windows && exists $use_system_pkg{$key}) {
1797 $opts{$key} = '/usr';
1798 print "Defaulting $key to $opts{$key}\n" if $opts{'verbose'};
1800 elsif ($is_windows && exists $use_win_default{$key}) {
1801 my $dir = $use_win_default{$key};
1803 if (-d $dir) {
1804 $opts{$key} = $dir;
1806 else {
1807 die "Default '$key' directory '$dir' not found.\nStopped";
1810 print "Defaulting $key to $opts{$key}\n" if $opts{'verbose'};
1812 elsif ($key eq 'java') {
1814 $opts{'java'} = default_java_home();
1816 if ($opts{'java'} eq '') {
1817 die "ERROR: --$key requires a value.\nStopped";
1820 elsif (exists $optdep{$key}->{env} && !$optdep{$key}->{may_be_blank}) {
1821 die "ERROR: --$key requires a value.\nStopped";
1826 sub has_feature {
1827 my $feat = shift;
1828 for my $f (@features) {
1829 my ($key, $value) = split(/=/, $f);
1830 if ($key eq $feat) {
1831 return 1;
1834 return 0;
1837 if ($opts{'java'}) {
1838 push(@features, 'java=1');
1840 my $feat = 'java_pre_jpms';
1841 unless (has_feature($feat)) {
1842 my $javac = File::Spec->catfile($opts{'java'}, 'bin', 'javac');
1843 open(my $vers, "\"$javac\" -version |");
1844 while (<$vers>) {
1845 if (/javac (\d+)/) {
1846 print "Found javac major version $1\n" if $opts{'verbose'};
1847 if ($1 > 1) {
1848 push(@features, "$feat=0");
1849 print "Feature $feat=0 due to Java version\n" if $opts{'verbose'};
1851 last;
1857 sub env_from_opt {
1858 my $key = shift;
1859 my $e = shift;
1860 my $notdir = shift;
1861 if ($opts{$key}) {
1862 setEnv($e, $opts{$key}, $notdir);
1866 # Enable MPC features and populate expected environment variables
1867 # for all required "optional" dependencies.
1868 for my $key (keys %optdep) {
1869 if (exists $opts{$key}) {
1870 print "Enabling $key\n" if $opts{'verbose'};
1871 my $e = $optdep{$key}->{env};
1872 my $s = $optdep{$key}->{sanity};
1873 my $m = $optdep{$key}->{mpc};
1874 if ($opts{$key} ne 'skip_version_check') {
1875 env_from_opt($key, $e) if defined $e;
1876 if (ref $s eq 'HASH') {
1877 my $ok = 0;
1878 for my $alt (keys %{$s}) {
1879 if (-r $targetEnv{$e} . '/' . $alt) {
1880 print "Found $key at $targetEnv{$e} using alternative $alt\n"
1881 if $opts{'verbose'};
1882 $ok = 1;
1883 push(@features, $s->{$alt}) if defined $s->{$alt};
1884 last;
1886 print "Didn't find $key at $targetEnv{$e} using alternative $alt\n"
1887 if $opts{'verbose'};
1889 unless ($ok) {
1890 die "ERROR: Can't find $key at $targetEnv{$e}.\nStopped";
1893 elsif ($s && !-r $targetEnv{$e} . '/' . $s) {
1894 die "ERROR: Can't find $key at $targetEnv{$e} (using $s).\nStopped";
1897 push(@features, $m) if $m;
1898 push(@platformmacros, $m) if $need_platform_macros{$key};
1902 if ($is_windows && !$opts{'optimize'}) { # look for nonstandard vcpkg layout
1903 for my $key ('xerces3', 'openssl') {
1904 my $env = $optdep{$key}->{env};
1905 if ($opts{$key} && $targetEnv{$env}) {
1906 if (-d $targetEnv{$env} . '/debug/lib') {
1907 print "Using debug/lib subdir for $key\n" if $opts{'verbose'};
1908 my $libenv = $env;
1909 $libenv =~ s/_?ROOT$/_LIBDIR/;
1910 setEnv($libenv, $targetEnv{$env} . '/debug/lib');
1911 $opts{$key . '-debugbin'} = 1;
1917 ## OpenSSL version-detection / feature-injection
1918 if ($opts{'openssl'} && $opts{'openssl'} ne 'skip_version_check') {
1919 my $ssl_version_file = $opts{'openssl'} . '/' . $optdep{'openssl'}->{sanity};
1921 open my $fh, '<', $ssl_version_file
1922 or die "ERROR: Failed to open '$ssl_version_file' for OpenSSL version detection.\nStopped";
1924 while (<$fh>) {
1925 if (/^\s*#\s*define\s*OPENSSL_VERSION_TEXT\s*"([^"]*)"/) {
1926 my $ver = $1;
1927 print "OpenSSL version detected: \"$ver\"\n" if $opts{'verbose'};
1928 if ($ver =~ /OpenSSL[\W]+1\.1/ || $ver =~ /OpenSSL[\W]+3\./) {
1929 print "OpenSSL 1.1+ MPC feature enabled\n" if $opts{'verbose'};
1930 push(@features, 'openssl11=1');
1932 last;
1937 sub win32_cmake_generator {
1938 my $ver = shift;
1940 my %map = ('vc9' => 'Visual Studio 9 2008',
1941 'vc10' => 'Visual Studio 10 2010',
1942 'vc11' => 'Visual Studio 11 2012',
1943 'vc12' => 'Visual Studio 12 2013',
1944 'vc14' => 'Visual Studio 14 2015',
1945 'vs2017' => 'Visual Studio 15 2017',
1946 'vs2019' => 'Visual Studio 16 2019',
1947 'vs2022' => 'Visual Studio 17 2022');
1949 die "ERROR: Unsupported Visual Studio version '$ver' supplied.\nStopped"
1950 if ! exists $map{$ver};
1952 return $map{$ver};
1955 if ($build_gtest) {
1956 print("Building GoogleTest...\n");
1958 my $cwd = Cwd::getcwd();
1959 my $build_dir = $opts{'gtest'} . $slash . 'build';
1960 my $install_dir = $build_dir . $slash . 'install';
1961 my $cmake = $opts{'cmake'};
1962 my @cmake_platform_args = $is_windows ?
1963 ('-Dgtest_force_shared_crt=ON',
1964 '-DCMAKE_CXX_FLAGS=/D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1',
1965 '-G', '"' . win32_cmake_generator($opts{'compiler_version'}) . '"',
1966 '-A', $opts{'compiler_target_architecture'}) : ();
1967 if ($opts{'nonstdcompiler'}) {
1968 push @cmake_platform_args, "-DCMAKE_CXX_COMPILER=$opts{'compiler'}";
1970 my @cmake_cmds = ([$cmake,
1971 "-DCMAKE_INSTALL_PREFIX=$install_dir",
1972 '-DCMAKE_INSTALL_LIBDIR=lib',
1973 '-DCMAKE_POSITION_INDEPENDENT_CODE=TRUE',
1974 @cmake_platform_args,
1975 '..'],
1976 ["$cmake", "--build", ".", "--target", "install"]);
1978 mkdir($build_dir)
1979 or die "ERROR '$!': failed to make directory $build_dir from $cwd.\nStopped"
1980 if ! -d $build_dir;
1982 mkdir($install_dir)
1983 or die "ERROR '$!': failed to make directory $install_dir from $cwd.\nStopped"
1984 if ! -d $install_dir;
1986 my $cd_build_dir = ChangeDir->new($build_dir);
1987 for my $cmd (@cmake_cmds) {
1988 run_command($cmd, capture => [undef, dump_on_failure => 1]) == 0
1989 or die "ERROR: Invoking @{$cmd} failed.\nStopped";
1992 print("Done Building GoogleTest\n");
1995 if (exists $opts{'jboss'} && !exists $opts{'java'}) {
1996 die "ERROR: --java is required for --jboss (OpenDDS JMS Provider).\nStopped";
1999 if (exists $opts{'jboss'} && !exists $opts{'ant'}) {
2000 die "ERROR: --ant is required for --jboss (OpenDDS JMS Provider).\nStopped";
2003 if (exists $opts{'wireshark-cmake'}) {
2004 if (exists $opts{'wireshark'}) {
2005 die "ERROR: --wireshark and --wireshark-cmake can not be used at the same time.\nStopped";
2008 if (! -d ($opts{'wireshark-build'} . '/' . $opts{'wireshark-lib'})) {
2009 if ($wireshark_lib_defaulted) {
2010 die "ERROR: The default value for wireshark-lib: " . $opts{'wireshark-lib'} .
2011 " does not exist, please supply wireshark-lib with the correct value";
2013 else {
2014 die "ERROR: The supplied wireshark-lib: " . $opts{'wireshark-lib'} . " does not exist.";
2019 if (exists $opts{'wireshark-cmake'} && !exists $opts{'wireshark-build'}) {
2020 die "ERROR: --wireshark-build are required with --wireshark-cmake.\nStopped";
2023 if (exists $opts{'wireshark-lib'} && !exists $opts{'wireshark-cmake'}) {
2024 die "ERROR: --wireshark-cmake and --wireshark-build is required for --wireshark-lib.\nStopped";
2027 if (exists $opts{'wireshark-build'} && !exists $opts{'wireshark-cmake'}) {
2028 die "ERROR: --wireshark-cmake is required for --wireshark-build.\nStopped";
2031 if ($opts{'glib'}) {
2032 if (!-r "$opts{'glib'}/lib/glib-2.0/include/glibconfig.h") {
2033 my $pc = `pkg-config --cflags-only-I glib-2.0`;
2034 chomp $pc;
2035 if ($pc =~ m!-I$opts{'glib'}/([-\w/]+)/glib-2.0/include!) {
2036 print "pkg-config found GLIB_LIB_DIR=$1\n" if $opts{'verbose'};
2037 setEnv('GLIB_LIB_DIR', $1);
2039 else {
2040 print "Failed to locate glibconfig.h using pkg-config ($pc). Set the " .
2041 "environment variable GLIB_LIB_DIR and re-run MPC.\n";
2046 sub push_env_dir {
2047 my ($buildEnvRef, $var, $path) = @_;
2048 if (exists $buildEnvRef->{$var}) {
2049 $buildEnvRef->{$var} .= $specific{'pathsep'} . $path;
2051 else {
2052 $buildEnvRef->{$var} = $path;
2056 sub push_path {
2057 my ($buildEnvRef, $path) = @_;
2058 push_env_dir($buildEnvRef, 'PATH', $path);
2061 sub push_libpath {
2062 my ($buildEnvRef, $path) = @_;
2063 my $build = $buildEnvRef->{'build'};
2064 my $platform = $opts{$build};
2065 my $libpathname = $platforminfo{$platform}->{'libpath'};
2066 push_env_dir($buildEnvRef, $libpathname, $path);
2069 # Qt5
2070 if (exists $opts{'qt'}) {
2071 my $qglobal = "QtCore" . $slash . "qglobal.h";
2072 my $qt_include;
2073 my $qt_bin; # location of Qt's code generators (like moc)
2074 my $qt_lib; # where linker should look for input (location of .lib or .so)
2075 my $qt_path; # add to PATH or LD_LIBRARY_PATH (location of .dll or .so.*)
2076 my $qt_bin_suffix = exists $ENV{'QT5_SUFFIX'} ? $ENV{'QT5_SUFFIX'} : '';
2077 my $qt_moc = "moc" . $qt_bin_suffix . $exeext;
2078 my $qt_default_suffix = "-qt5";
2079 my $qt_moc_default_suffix = "moc" . $qt_default_suffix . $exeext;
2080 my $qt_help_mesg = ", stopped\nPlease either install Qt or provide the correct " .
2081 "Qt5 locations (see docs/qt.md for details).\n";
2083 if ($try_to_use_qt_system_pkg) {
2084 # Try to use pkg-config to get Qt locations
2085 `pkg-config --print-variables Qt5Core`;
2086 if ($?) {
2087 die "ERROR: Trying to use system Qt package but could not confirm " .
2088 "Qt and/or pkg-config exists on the system$qt_help_mesg";
2090 $qt_include = `pkg-config --variable=includedir Qt5Core`;
2091 die "ERROR: could not resolve system Qt include location$qt_help_mesg" if ($?);
2092 chomp $qt_include;
2093 $qt_bin = `pkg-config --variable=host_bins Qt5Core`;
2094 die "ERROR: could not resolve system Qt build tools location$qt_help_mesg" if ($?);
2095 chomp $qt_bin;
2096 $qt_lib = `pkg-config --variable=libdir Qt5Core`;
2097 die "ERROR: could not resolve system Qt lib location$qt_help_mesg" if ($?);
2098 chomp $qt_lib;
2100 else {
2101 if (exists $opts{'qt-include'}) {
2102 if (!length($opts{'qt-include'})) {
2103 die "ERROR: --qt-include requires an argument$qt_help_mesg";
2105 $qt_include = $opts{'qt-include'};
2107 else {
2108 $qt_include = $targetEnv{'QTDIR'} . $slash . 'include';
2111 if (exists $ENV{'QT5_BINDIR'}) {
2112 $qt_bin = $ENV{'QT5_BINDIR'};
2114 else {
2115 $qt_bin = $targetEnv{'QTDIR'} . $slash . 'bin';
2118 if (exists $ENV{'QT5_LIBDIR'}) {
2119 $qt_lib = $ENV{'QT5_LIBDIR'};
2121 else {
2122 $qt_lib = $targetEnv{'QTDIR'} . $slash . ($is_windows ? 'bin' : 'lib');
2125 $qt_path = $qt_lib;
2127 # Try to guess if Qt vcpkg is being used
2128 my $vcpkg_tools = join($slash, $targetEnv{'QTDIR'}, "tools", "qt5");
2129 my $assume_vcpkg =
2130 (-r $vcpkg_tools . $slash . 'bin' . $slash . $qt_moc) &&
2131 !exists $ENV{'QT5_LIBDIR'} &&
2132 !exists $ENV{'QT5_BINDIR'};
2133 if ($assume_vcpkg) {
2134 $qt_bin = $vcpkg_tools . $slash . 'bin';
2135 if ($debug) {
2136 $qt_lib = join($slash, $targetEnv{'QTDIR'}, "debug", "lib");
2137 $qt_path = join($slash, $targetEnv{'QTDIR'}, "debug",
2138 ($is_windows ? 'bin' : 'lib'));
2140 else {
2141 $qt_lib = join($slash, $targetEnv{'QTDIR'}, 'lib');
2145 # Check for Qt Headers using qglobal.h
2146 # Also try to detect if Qt Headers were just put in the include directory or
2147 # put in qt5 directory inside the include directory. For example, the former
2148 # might happen on Windows with prebuilt Qt while the latter might be the
2149 # case with Linux with Qt installed from package manager.
2150 if (! -r $qt_include . $slash . $qglobal) {
2151 my $qt_subdir = $qt_include . $slash . 'qt5';
2152 if (-r $qt_subdir . $slash . $qglobal) {
2153 $qt_include = $qt_subdir;
2155 else {
2156 die "ERROR: Could not find Qt headers at $qt_include" .
2157 " ($qglobal could not be found)$qt_help_mesg";
2161 # Check for Qt Tools using moc
2162 if (! -r $qt_bin . $slash . $qt_moc) {
2163 # If QT5_SUFFIX isn't defined, maybe it needs a suffix
2164 if ((!exists $ENV{'QT5_SUFFIX'}) &&
2165 (-r $qt_bin . $slash . $qt_moc_default_suffix)) {
2166 $qt_bin_suffix = $qt_default_suffix;
2168 else {
2169 die "ERROR: Could not find Qt build tools at $qt_bin" .
2170 " ($qt_moc could not be found)$qt_help_mesg";
2174 setEnv('QT5_INCDIR', $qt_include);
2175 setEnv('QT5_BINDIR', $qt_bin);
2176 setEnv('QT5_SUFFIX', $qt_bin_suffix);
2177 setEnv('QT5_LIBDIR', $qt_lib);
2178 push_libpath(\%targetEnv, $qt_path);
2180 elsif (exists $opts{'qt-include'}) {
2181 die "ERROR: --qt-include requires --qt, stopped\n";
2184 sub write_host_workspace {
2185 my %buildEnv = %{shift()};
2186 my $MWC = backup_and_open($buildEnv{'DDS_ROOT'} . '/host_tools.mwc');
2187 print $MWC <<'EOT';
2188 workspace {
2189 $(ACE_ROOT)/ace/ace.mpc
2190 $(ACE_ROOT)/apps/gperf/src
2191 $(TAO_ROOT)/TAO_IDL
2192 dds/DCPS/OpenDDS_Util.mpc
2193 dds/idl
2194 java/idl2jni/codegen
2196 print $MWC " \$(TAO_ROOT)/tao/tao.mpc\n" if $opts{'safety-profile'};
2197 print $MWC "}\n";
2198 $MWC->close;
2199 print "Wrote host_tools.mwc in $buildEnv{'DDS_ROOT'}\n" if $opts{'verbose'};
2200 dump_and_unlink($MWC) if $opts{'dry-run'};
2203 sub mergeToEnv {
2204 my $buildEnv = shift;
2205 for my $k (keys %{$buildEnv}) {
2206 next if $k eq 'build';
2207 if ($buildEnv->{$k} =~
2208 /^\Q$specific{'refpre'}\E$k\Q$specific{'refpost'}\E(.*)/) {
2209 if ($1 ne '') {
2210 $ENV{$k} .= $1;
2211 print "ENV: Appending $1 to $k\n" if $opts{'verbose'};
2214 else {
2215 $ENV{$k} = $buildEnv->{$k};
2216 print "ENV: Setting $k to $buildEnv->{$k}\n" if $opts{'verbose'};
2221 sub disable_feature {
2222 my($featureArray, $feature) = @_;
2223 $feature =~ s/-/_/g;
2224 push(@{$featureArray}, "$feature=0");
2227 sub get_default_features {
2228 my $buildEnv = shift;
2230 my %default_features;
2231 my $file = "$buildEnv->{ACE_ROOT}/bin/MakeProjectCreator/config/default.features";
2232 if (-e $file) {
2233 open(my $fh, '<', $file) or die "ERROR: Could not open $file: $!";
2234 while (my $row = <$fh>) {
2235 chomp $row;
2236 $row =~ s/\/\/.*//;
2237 $row =~ s/^\s+//;
2238 $row =~ s/\s+$//;
2239 next if $row eq '';
2240 my ($key, $value) = split(/\s*=\s*/, $row);
2241 $default_features{$key} = $value;
2244 return %default_features;
2247 sub get_requested_features {
2248 my %requested_features;
2249 for my $f (@features) {
2250 my ($key, $value) = split(/=/, $f);
2251 $value = "1" unless defined $value;
2252 $requested_features{$key} = $value;
2254 return %requested_features;
2257 sub get_features {
2258 my $buildEnv = shift;
2260 my %features = get_default_features($buildEnv);
2261 my %requested_features = get_requested_features();
2262 for my $key (keys(%requested_features)) {
2263 $features{$key} = $requested_features{$key};
2265 return %features;
2268 sub write_opendds_mwc {
2269 my $root = shift;
2270 my $args = shift;
2272 $args = join(', ', (map {perlstring($_)} @{$args}));
2274 my $path = "$root/bin/opendds_mwc.pl";
2275 my $fd = backup_and_open($path);
2276 print $fd <<"EOF";
2277 #!/usr/bin/env perl
2278 if (!defined(\$ENV{ACE_ROOT}) || !defined(\$ENV{MPC_ROOT})) {
2279 die("The enviroment needs to be setup.");
2281 system('perl', "\$ENV{ACE_ROOT}/bin/mwc.pl", $args, \@ARGV) == 0
2282 or die("Failed to run mwc: \$!");
2284 $fd->close();
2285 print "Wrote $path\n" if $opts{'verbose'};
2286 dump_and_unlink($fd) if $opts{'dry-run'};
2288 chmod(0775, $path);
2291 my $buildtao;
2292 sub generate_workspace {
2293 my $buildEnv = shift;
2294 $buildtao = $force_ace_tao;
2295 my $is_target = $buildEnv->{'build'} eq 'target';
2297 for my $feat (qw/built-in-topics ownership-profile/) {
2298 if (exists $opts{$feat} && !$opts{$feat}) {
2299 disable_feature(\@features, $feat);
2303 if ($opts{'security'}) {
2304 disable_feature(\@features, 'no-opendds-security');
2307 # default of these depends on whether we are doing a safety-profile build
2308 for my $feat (qw/content-subscription content-filtered-topic
2309 multi-topic query-condition ownership-kind-exclusive
2310 object-model-profile persistence-profile
2311 no-opendds-safety-profile/) {
2312 if (defined $opts{'no-opendds-safety-profile'}) {
2313 if (!(exists $opts{$feat} && $opts{$feat})) {
2314 disable_feature(\@features, $feat);
2317 elsif (exists $opts{$feat} && !$opts{$feat}) {
2318 disable_feature(\@features, $feat);
2322 if ($is_target && $opts{'target'} eq 'android') {
2323 # We can't detect C++11 compatibility in cross compilers at the moment,
2324 # but all the NDKs we officially support are C++11+ by default, so enable
2325 # C++11 features unless an explicit 'no_cxx11' feature already is set.
2326 my $feat = 'no_cxx11';
2327 unless (has_feature($feat)) {
2328 disable_feature(\@features, $feat);
2329 print "Setting $feat=0 for Android\n";
2332 # Disable including $JAVA_HOME/include on Android. Android NDK includes a
2333 # jni.h
2334 disable_feature(\@features, 'jni_include')
2337 my $mpctype = (!$is_windows || ($cross_compile && $is_target))
2338 ? 'gnuace' : $opts{'compiler_version'};
2340 if ($mpctype eq 'gnuace') {
2341 my $UM;
2342 if (@features || $cross_compile) {
2343 print "Writing $buildEnv->{'DDS_ROOT'}/user_macros.GNU\n"
2344 if $opts{'verbose'};
2345 $UM = backup_and_open($buildEnv->{'DDS_ROOT'} . '/user_macros.GNU');
2347 if ($is_target) {
2348 for my $feat (@features) {
2349 my $key = $feat;
2350 $key =~ s/=.*//;
2351 print $UM ($feat =~ /=/ ? $feat : "$feat=1"), "\n"
2352 unless $need_platform_macros{$key};
2354 if ($cross_compile) {
2355 print $UM <<'EOT';
2356 OPENDDS_IDL = $(HOST_DDS)/bin/opendds_idl
2357 OPENDDS_IDL_DEP = $(OPENDDS_IDL)$(HOST_EXE_EXT)
2358 IDL2JNI = $(HOST_DDS)/bin/idl2jni
2359 IDL2JNI_DEP = $(IDL2JNI)$(HOST_EXE_EXT)
2363 $UM->close if $UM;
2364 dump_and_unlink($UM) if $UM && $opts{'dry-run'};
2367 if ($opts{'safety-profile'} && $is_target) {
2368 $buildtao = 0; # TAO will be built separately
2370 else {
2371 # build tao if tao_idl does not exist
2372 $buildtao = $buildtao || !-x "$buildEnv->{'ACE_ROOT'}/bin/tao_idl$exeext";
2375 locate_mpc($ace_src);
2377 # Append to default.features
2378 if ($is_target) {
2379 push(@features, 'cross_compile') if $cross_compile;
2381 if ($platforminfo{$opts{'target'}}->{'needs_i2jrt_corba'} && $opts{'java'}) {
2382 if ($opts{'verbose'}) {
2383 print "Target platform needs i2jrt_corba.jar, forcing java_pre_jpms=0 to get it\n";
2385 my $found = 0;
2386 for my $i (0 .. scalar $#features) {
2387 if ($features[$i] =~ /^java_pre_jpms/) {
2388 $features[$i] = 'java_pre_jpms=0';
2389 my $found = 1;
2392 if (!$found) {
2393 push(@features, "java_pre_jpms=0");
2397 if ($opts{'java'} && $cross_compile) {
2398 push(@features, "jni_check=0");
2401 if ($buildtao && @features) {
2402 my $df_file = $buildEnv->{'ACE_ROOT'} .
2403 '/bin/MakeProjectCreator/config/default.features';
2404 my $DF = $opts{'dry-run'} ? File::Temp->new()
2405 : new FileHandle(">>$df_file");
2406 for my $f (@features) {
2407 print $DF ($f =~ /=/ ? $f : "$f=1"), "\n";
2409 $DF->close;
2410 if ($opts{'verbose'}) {
2411 print '' . ($wrote_df ? 'Appended to' : 'Wrote') .
2412 " $buildEnv->{'ACE_ROOT'}/.../default.features\n";
2414 dump_and_unlink($DF) if $opts{'dry-run'};
2417 if (!$buildtao && !defined $opts{'safety-profile'}) {
2418 # Check for compatibility between the requested features/macros
2419 # and the pre-built ACE/TAO.
2420 my %existing_features = get_default_features($buildEnv);
2421 my %requested_features = get_requested_features();
2422 for my $f (@ace_features) {
2423 if (defined $requested_features{$f}) {
2424 if (!defined $existing_features{$f}) {
2425 die "ERROR: Requested feature $f not defined for ACE/TAO";
2427 elsif ($requested_features{$f} != $existing_features{$f}) {
2428 die "ERROR: Feature $f set to $requested_features{$f} but has " .
2429 "value $existing_features{$f} in ACE/TAO";
2434 my $file = "$buildEnv->{'ACE_ROOT'}/include/makeinclude/platform_macros.GNU";
2435 my %existing_macros;
2436 if (-e $file) {
2437 open(my $fh, '<', $file) or die "ERROR: Could not open $file: $!";
2438 while (my $row = <$fh>) {
2439 chomp $row;
2440 $row =~ s/\s*#.*$//;
2441 if ($row =~ /^\s*(\w+)\s*[:?+]?=\s*(.*)$/) {
2442 my ($key, $value) = ($1, $2);
2443 $existing_macros{$key} = $value;
2446 my %requested_macros;
2447 for my $f (@platformmacros, @{$opts{'macros'}}) {
2448 my ($key, $value) = split(/=/, $f);
2449 $value = "1" unless defined $value;
2450 $requested_macros{$key} = $value;
2452 for my $key (@ace_macros) {
2453 if (exists $opts{$key}) {
2454 my $macro = ($key eq 'static') ? 'static_libs_only' : $key;
2455 $requested_macros{$macro} = $opts{$key};
2458 for my $f (@ace_macros) {
2459 if (defined $requested_macros{$f}) {
2460 if (!defined $existing_macros{$f}) {
2461 die "ERROR: Requested macro $f not defined for ACE/TAO";
2463 elsif ($requested_macros{$f} != $existing_macros{$f}) {
2464 die "ERROR: Macro $f set to $requested_macros{$f} but has " .
2465 "value $existing_macros{$f} in ACE/TAO";
2473 my $custom_ws = "OpenDDS_custom.mwc";
2474 if (exists($opts{'workspace'})) {
2475 backup_and_copy($opts{'workspace'}, $custom_ws);
2478 my $ws;
2479 if (-r "$buildEnv->{'DDS_ROOT'}/host_tools.mwc") {
2480 $ws = 'host_tools.mwc';
2482 elsif (-r $custom_ws) {
2483 $ws = $custom_ws;
2485 elsif (!$tests && $buildtao) {
2486 $ws = 'DDS_TAOv2.mwc';
2488 elsif (!$tests && !$buildtao) {
2489 $ws = 'DDS_no_tests.mwc';
2491 elsif ($buildtao) {
2492 $ws = 'DDS_TAOv2_all.mwc';
2494 else {
2495 $ws = 'DDS.mwc';
2498 my $static = (($opts{'static'} && $is_windows) ||
2499 ($cross_compile && $buildEnv->{'build'} eq 'host' && $mpctype ne 'gnuace'));
2501 # We are not using CIAO or DAnCE, but MPC.cfg expands $CIAO_ROOT and
2502 # $DANCE_ROOT so leaving them empty/undefined would cause /MPC/config
2503 # to be on the include path for .mpb files.
2504 for my $var ('CIAO_ROOT', 'DANCE_ROOT') {
2505 $buildEnv->{$var} = 'unused' unless defined $buildEnv->{$var};
2508 my %savedEnv = %ENV;
2509 print "ENV: saving current environment\n" if $opts{'verbose'};
2510 mergeToEnv($buildEnv);
2512 my @mwc_common_args = ('-type', $mpctype, @mpcopts);
2513 if ($static) {
2514 push(@mwc_common_args, '-static');
2517 if (!$buildtao) {
2518 my %existing_features = get_default_features($buildEnv);
2519 my %requested_features = get_requested_features();
2520 my @features_to_add;
2521 for my $feature (keys(%requested_features)) {
2522 my $value = $requested_features{$feature};
2523 push(@features_to_add, "$feature=$value") unless exists($existing_features{$feature});
2525 if (@features_to_add) {
2526 push(@mwc_common_args, '-features', join(',', @features_to_add));
2530 # Generate our own mwc wrapper script
2531 write_opendds_mwc($buildEnv->{'DDS_ROOT'}, [@mwc_common_args]);
2533 my @mwc = ('perl', "$ENV{ACE_ROOT}/bin/mwc.pl");
2534 print 'Running MPC to generate ', ($mpctype eq 'gnuace' ? 'makefiles' :
2535 'project files'), ".\n";
2536 if (run_command([@mwc, @mwc_common_args, "$buildEnv->{'DDS_ROOT'}$slash$ws"])) {
2537 die "ERROR: Error from MPC, stopped";
2539 $buildEnv->{'mpctype'} = $mpctype;
2541 # If this is a target safety profile build
2542 if (defined $opts{'no-opendds-safety-profile'}) {
2543 # Generate ACE workspace separately, to exclude TAO
2544 if (run_command([@mwc, @mwc_common_args, "$buildEnv->{'ACE_ROOT'}/ace"])) {
2545 die "ERROR: Error from MPC, stopped";
2549 %ENV = %savedEnv;
2550 print "ENV: restoring previous environment\n" if $opts{'verbose'};
2552 $ws =~ s/\.mwc$/.sln/;
2553 $opts{'solution_file'} = $ws;
2556 sub convert_environment {
2557 my $val = shift;
2558 $$val =~ s/\\/\//g;
2559 $$val =~ s/%(\w+)%/\$\{$1\}/g;
2562 sub write_environment {
2563 my %buildEnv = %{shift()};
2564 my $dir = shift;
2565 my $MK = undef;
2566 if ($buildEnv{'mpctype'} eq 'gnuace') {
2567 if (!$opts{'dry-run'}) {
2568 move($dir . '/GNUmakefile', $dir . '/GNUmakefile.dist');
2570 $MK = backup_and_open($dir . '/GNUmakefile');
2573 my $SE = backup_and_open($dir . '/setenv.' . $specific{'ext'});
2575 my $args_ = join(' ', map {/ / ? ('"' . $_ . '"') : $_} @ARGS);
2576 print $SE "$specific{'comment'} OpenDDS configure script: $0 $args_\n";
2577 print $MK "$specific{'comment'} OpenDDS configure script: $0 $args_\n" if $MK;
2579 for my $key (sort(keys(%buildEnv))) {
2580 if ($key ne 'build' && $key ne 'mpctype') {
2581 my $value = $buildEnv{$key};
2582 $value =~ s/^"(.*)"$/$1/g;
2584 if ($is_windows) {
2585 print $SE "set \"$key=$value\"\n";
2587 else {
2588 my $posix_value = $value;
2589 $posix_value =~ s/"/\\"/g;
2590 print $SE "export $key=\"$posix_value\"\n";
2593 if ($MK) {
2594 convert_environment(\$value) if $is_windows;
2595 print $MK "export $key := $value\n";
2600 $SE->close;
2601 print "Wrote $dir/setenv.$specific{'ext'}\n" if $opts{'verbose'};
2602 dump_and_unlink($SE) if $opts{'dry-run'};
2604 if ($MK) {
2605 print $MK "include GNUmakefile.dist\n";
2606 $MK->close;
2607 print "Wrote $dir/GNUmakefile, wrapping original GNUmakefile.dist\n"
2608 if $opts{'verbose'};
2609 dump_and_unlink($MK) if $opts{'dry-run'};
2613 sub check_mac_version {
2614 my $buildEnvRef = shift;
2615 if (-r "$buildEnvRef->{ACE_ROOT}/ace/config-macosx.h") {
2616 my $cfg = new FileHandle("$buildEnvRef->{ACE_ROOT}/ace/config-macosx.h");
2617 while (<$cfg>) {
2618 if (/__MAC_OS_X_VERSION_MAX_ALLOWED/) {
2619 return;
2624 die "ERROR: This version of ACE doesn't contain the required configuration " .
2625 "files for Apple macOS. Stopped";
2628 sub configure_build {
2629 my $buildEnvRef = shift;
2630 if (-r "$buildEnvRef->{ACE_ROOT}/ace/config.h" && !$force_ace_tao) {
2631 print "ACE_ROOT/ace/config.h exists, skipping configuration of ACE+TAO\n";
2633 else {
2634 check_mac_version($buildEnvRef) if $opts{'host'} eq 'macosx';
2636 write_config_h($buildEnvRef);
2637 write_default_features($buildEnvRef);
2638 write_platform_macros($buildEnvRef);
2640 write_opendds_configh($buildEnvRef);
2641 generate_workspace($buildEnvRef);
2642 write_environment($buildEnvRef, $buildEnvRef->{'DDS_ROOT'});
2644 if ($buildEnvRef->{'build'} eq 'target') {
2645 write_cmake_file($buildEnvRef);
2649 sub write_cross_compile_makefile {
2650 my $MF = backup_and_open('GNUmakefile');
2651 my $safety_profile_step = $opts{'safety-profile'} ?
2652 "\tcd $targetEnv{'ACE_ROOT'}/ace && " .
2653 '$(MAKE) $(if $(filter all,$@),ACE,$@)' . "\n" : '';
2654 print $MF <<"EOT";
2655 # OpenDDS configure script: $0 @ARGS
2656 all clean realclean depend:
2657 \tcd build/host && \$(MAKE) \$@
2658 $safety_profile_step\tcd build/target && \$(MAKE) \$@
2659 .PHONY: all clean realclean depend
2661 $MF->close;
2662 print "Wrote top-level GNUmakefile for cross-compile\n" if $opts{'verbose'};
2663 dump_and_unlink($MF) if $opts{'dry-run'};
2666 sub nested {
2667 my($sub, $top) = @_;
2668 my $subdir = $opts{'dry-run'} ? $sub : Cwd::abs_path($sub);
2669 my $parent = $opts{'dry-run'} ? $top : Cwd::abs_path($top);
2670 if ($opts{'dry-run'} && $top eq '.' && looksRelative($sub)) {
2671 return $slash . $sub;
2673 if (index($subdir, $parent) == 0) {
2674 return substr($subdir, length($parent));
2676 return undef;
2679 sub add_dependency_paths {
2680 my %dirs;
2681 for my $key ('openssl', 'xerces3') {
2682 if ($opts{$key}) {
2683 next if $opts{$key} eq '/usr' || $opts{$key} eq 'skip_version_check';
2684 my $suffix = $slash . 'bin';
2685 if ($opts{$key . '-debugbin'}) {
2686 $suffix = $slash . 'debug' . $suffix;
2688 $dirs{$targetEnv{$optdep{$key}->{env}} . $suffix} = 1;
2691 for my $d (keys %dirs) {
2692 push_libpath(\%targetEnv, $d);
2695 if ($opts{'openssl'} && $ENV{'SSL_LIBDIR'}) {
2696 push_libpath(\%targetEnv, $ENV{'SSL_LIBDIR'});
2700 my $cloned_build = $cross_compile && $build_host_tools;
2701 if ($cloned_build) {
2702 clone_host_and_target('.');
2703 setHostEnv('DDS_ROOT', 'build/host');
2704 setEnv('HOST_DDS', 'build/host');
2705 setEnv('DDS_ROOT', 'build/target');
2707 my $tao_clone = $opts{'safety-profile'} ? 'host' : 'target';
2708 my $ace_sub_dds = nested($ace_src, '.');
2709 my $tao_sub_dds = nested($tao_src, '.');
2710 my $tao_sub_ace = nested($tao_src, $ace_src);
2712 if ($ace_sub_dds) {
2713 setHostEnv('ACE_ROOT', 'build/host' . $ace_sub_dds);
2714 setEnv('HOST_ACE', 'build/host' . $ace_sub_dds);
2715 setEnv('ACE_ROOT', 'build/target' . $ace_sub_dds);
2717 else {
2718 clone_host_and_target($ace_src);
2719 setHostEnv('ACE_ROOT', $ace_src . '/build/host');
2720 setEnv('HOST_ACE', $ace_src . '/build/host');
2721 setEnv('ACE_ROOT', $ace_src . '/build/target');
2722 if ($tao_sub_ace) {
2723 setHostEnv('TAO_ROOT', $ace_src . '/build/host' . $tao_sub_ace);
2724 setEnv('TAO_ROOT', $ace_src . '/build/' . $tao_clone . $tao_sub_ace);
2728 if ($tao_sub_dds) {
2729 setHostEnv('TAO_ROOT', 'build/host' . $tao_sub_dds);
2730 setEnv('TAO_ROOT', 'build/' . $tao_clone . $tao_sub_dds);
2732 elsif (!$tao_sub_ace) {
2733 if ($opts{'safety-profile'}) {
2734 setHostEnv('TAO_ROOT', $tao_src);
2735 setEnv('TAO_ROOT', $tao_src);
2737 else {
2738 clone_host_and_target($tao_src);
2739 setHostEnv('TAO_ROOT', $tao_src . '/build/host');
2740 setEnv('TAO_ROOT', $tao_src . '/build/target');
2744 push_path(\%targetEnv, $hostEnv{'ACE_ROOT'} . $slash . 'bin');
2745 push_path(\%targetEnv, $hostEnv{'DDS_ROOT'} . $slash . 'bin');
2747 print "Cross-compile configuring host\n";
2749 write_host_workspace(\%hostEnv); # host_tools.mwc
2750 configure_build(\%hostEnv);
2752 print "Cross-compile configuring target\n";
2754 if ($opts{'safety-profile'}) {
2755 $opts{'no-opendds-safety-profile'} = 0; # only set for target
2758 unless ($opts{'static'}) {
2759 push_libpath(\%targetEnv, $targetEnv{'ACE_ROOT'} . $slash . 'lib');
2760 push_libpath(\%targetEnv, $targetEnv{'DDS_ROOT'} . $slash . 'lib');
2762 add_dependency_paths();
2765 configure_build(\%targetEnv);
2767 write_environment(\%targetEnv, $targetEnv{'ACE_ROOT'} . '/ace')
2768 if $opts{'safety-profile'};
2770 write_cross_compile_makefile() if !$is_windows;
2772 else { # does not require cloned builds
2773 setEnv('ACE_ROOT', $ace_src);
2774 setEnv('TAO_ROOT', $tao_src);
2775 setEnv('DDS_ROOT', '.');
2777 if (!$build_host_tools && $opts{'host-tools'}) {
2778 setEnv('HOST_DDS', $opts{'host-tools'});
2781 push_path(\%targetEnv, $targetEnv{'ACE_ROOT'} . $slash . 'bin');
2782 push_path(\%targetEnv, $targetEnv{'DDS_ROOT'} . $slash . 'bin');
2784 unless ($opts{'static'}) {
2785 push_libpath(\%targetEnv, $targetEnv{'ACE_ROOT'} . $slash . 'lib');
2786 push_libpath(\%targetEnv, $targetEnv{'DDS_ROOT'} . $slash . 'lib');
2788 add_dependency_paths();
2791 if ($host_tools_only) {
2792 write_host_workspace(\%targetEnv);
2795 configure_build(\%targetEnv);
2798 print "Completed configuring OpenDDS, next ";
2799 if (!$is_windows || ($cross_compile && !$cloned_build)) {
2800 print "run '", (($^O eq 'freebsd') ? 'g' : ''), "make'";
2802 elsif ($cloned_build) {
2803 print "compile build\\host\\host_tools.sln\n",
2804 "using the environment in build\\host\\setenv.cmd.\n",
2805 "Then build OpenDDS for $opts{'target'} by running make in ",
2806 "build\\target.\n";
2807 exit 0;
2809 else {
2810 print "build '", $opts{'solution_file'}, "' using the '",
2811 ($opts{'optimize'} ? 'Release' : 'Debug'), "' configuration";
2814 print " to compile ",
2815 ($cloned_build ? "\nthe host and target builds of " : ''),
2816 "OpenDDS", (($buildtao || $cloned_build) ? ' and ACE+TAO' : '') ,".\n",
2817 "You can use the generated setenv.$specific{'ext'} script ",
2818 ($cloned_build ?
2819 "in the\nbuild${slash}host and build${slash}target dirs " : ''),
2820 "to set environment\nvariables for future shell sessions.\n";
2821 if ($is_windows && !$cross_compile) {
2822 print "Start Visual Studio from this command prompt so that it inherits ",
2823 "the correct\nenvironment variables. Try running \"devenv $opts{'solution_file'}\".\n";
2826 sub write_cmake_file {
2827 my $buildEnv = shift();
2829 my $module_dir = "$buildEnv->{'DDS_ROOT'}/cmake";
2830 mkpath($module_dir) unless -d $module_dir;
2831 my $fh = backup_and_open($module_dir . '/config.cmake');
2833 sub whack_to_slash {
2834 my $s = shift;
2835 $s =~ s|\\|/|g;
2836 return $s;
2839 sub to_cmake_scalar {
2840 my $val = shift;
2841 return 'ON' if "$val" eq '1';
2842 return 'OFF' if "$val" eq '0';
2843 return '"' . whack_to_slash($val) . '"';
2846 sub to_cmake_list {
2847 my @list = @{$_[0]};
2848 return '"' . join(';', map {whack_to_slash($_)} @list) . '"';
2851 sub to_cmake_value {
2852 my $value = shift();
2854 my $ref_type = ref($value);
2855 if ($ref_type eq 'ARRAY') {
2856 return to_cmake_list($value);
2858 elsif ($ref_type eq '') {
2859 return to_cmake_scalar($value);
2861 else {
2862 die "ERROR: to_cmake_value invalid value type, stopped";
2866 sub print_cmake_config {
2867 my $fh = shift();
2868 my $name = shift();
2869 my $value = shift();
2871 $name = uc("OPENDDS_$name");
2872 $name =~ s/-/_/g;
2874 print $fh "set($name ", to_cmake_value($value), ")\n";
2877 sub generate_config_cmake {
2878 my $fh = shift();
2879 my $buildEnv = shift();
2881 print_cmake_config($fh, 'MPC', Cwd::abs_path($buildEnv->{'MPC_ROOT'}));
2882 print_cmake_config($fh, 'ACE', Cwd::abs_path($buildEnv->{'ACE_ROOT'}));
2883 print_cmake_config($fh, 'TAO', Cwd::abs_path($buildEnv->{'TAO_ROOT'}));
2885 print $fh "\n# Based on MPC features\n";
2886 my @configs = (
2888 name => 'CXX11',
2889 feature => 'no_cxx11',
2890 inverted => 1,
2893 name => 'WCHAR',
2894 feature => 'uses_wchar',
2897 name => 'IPV6',
2898 feature => 'ipv6',
2901 name => 'SAFETY_PROFILE',
2902 feature => 'no_opendds_safety_profile',
2903 inverted => 1,
2906 name => 'VERSIONED_NAMESPACE',
2907 feature => 'versioned_namespace',
2910 name => 'SUPPRESS_ANYS',
2911 feature => 'dds_suppress_anys',
2912 default => 1,
2915 name => 'COVERAGE',
2916 feature => 'dds_non_coverage',
2917 inverted => 1,
2920 name => 'TAO_CORBA_E_COMPACT',
2921 feature => 'corba_e_compact',
2924 name => 'TAO_CORBA_E_MICRO',
2925 feature => 'corba_e_micro',
2928 name => 'TAO_MINIMUM_CORBA',
2929 feature => 'minimum_corba',
2932 name => 'TAO_IIOP',
2933 feature => 'tao_no_iiop',
2934 inverted => 1,
2935 default => 1,
2938 name => 'TAO_GEN_OSTREAM',
2939 feature => 'gen_ostream',
2942 name => 'TAO_OPTIMIZE_COLLOCATED_INVOCATIONS',
2943 feature => 'optimize_collocated_invocations',
2944 default => 1,
2947 my %features = get_features($buildEnv);
2948 for my $config (@configs) {
2949 my $inverted = $config->{inverted} // 0;
2950 my $default = $config->{default} // 0;
2951 my $enabled;
2952 if (exists($features{$config->{feature}})) {
2953 $enabled = $features{$config->{feature}} eq "1" ? !$inverted : $inverted;
2955 else {
2956 $enabled = $default;
2958 print_cmake_config($fh, $config->{name}, $enabled);
2961 if (defined($cxx_std)) {
2962 print_cmake_config($fh, 'OPENDDS_CXX_STD', $cxx_std);
2965 # TODO(iguessthislldo): Move to a smarter system that can use existing
2966 # ACE/TAO and that works with cross compiling.
2967 print $fh "\n# Based on configure script options\n";
2968 my %opts_to_use = (
2969 'built-in-topics' => 1,
2970 'compiler' => '',
2971 'content-filtered-topic' => 1,
2972 'content-subscription' => 1,
2973 'debug' => 1,
2974 'gtest' => '',
2975 'inline' => 1,
2976 'java' => '',
2977 'multi-topic' => 1,
2978 'object-model-profile' => 1,
2979 'openssl' => '',
2980 'ownership-kind-exclusive' => 1,
2981 'ownership-profile' => 1,
2982 'persistence-profile' => 1,
2983 'qt' => '',
2984 'query-condition' => 1,
2985 'rapidjson' => '',
2986 'security' => 0,
2987 'static' => 0,
2988 'std' => '',
2989 'xerces3' => '',
2991 for my $opt (sort(keys(%opts_to_use))) {
2992 print_cmake_config($fh, $opt, exists($opts{$opt}) ? $opts{$opt} : $opts_to_use{$opt});
2994 print_cmake_config($fh, 'features', \@features);
2995 print_cmake_config($fh, 'mpcopts', \@mpcopts);
2997 print $fh "\n# Sanitizers\n";
2998 for my $name (sort(keys(%all_sanitizers))) {
2999 print_cmake_config($fh, $name, exists($enabled_sanitizers{$name}) ? 1 : 0);
3001 if (scalar(@fsanitize)) {
3002 print_cmake_config($fh, 'SANITIZER_COMPILER_ARGS', \@sanitizer_compiler_args);
3003 print_cmake_config($fh, 'SANITIZER_LINKER_ARGS', \@sanitizer_linker_args);
3007 print $fh <<"EOF";
3008 # Contains configuration variables for the FindOpenDDS cmake module based on
3009 # detected defaults and/or user-supplied switches. Feel free to edit them as
3010 # necessary, but keep in mind that they will be moved to config.cmake.bak.*
3011 # the next time the \$DDS_ROOT/configure script is executed.
3013 # THIS FILE WAS AUTO-GENERATED BY: $0 @ARGS
3016 generate_config_cmake($fh, $buildEnv);
3017 dump_and_unlink($fh) if $opts{'dry-run'};