Merge pull request #4655 from iguessthislldo/igtd/rtps-ports
[OpenDDS.git] / configure
blob5fc0993d5e8bdcc9711fdc10b1427241ffcad9e3
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'},
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-github-latest!', 'Clone latest ACE/TAO/MPC from GitHub (no)',
228 'force-ace-tao', 'Force configuration of ACE/TAO (no)',
229 'no-disable-deprecated', 'Turn off disabling deprecated interfaces when' .
230 $argIndent . 'configuring ACE/TAO (no)',
232 ['Advanced configuration:',
233 'configh=s@', 'Extra text for config.h',
234 'macros=s@', 'Extra text for platform_macros.GNU',
235 'features=s@', 'Extra text for default.features',
236 'mpcopts=s@', 'Extra command-line options for MPC' .
237 $argIndent . 'This option can be given multiple times' .
238 $argIndent . 'For example:' .
239 $argIndent . ' --mpcopts=-value_template --mpcopts=build_flags+="-Wall -Werror"' .
240 $argIndent . 'turns into the following arguments for MPC:' .
241 $argIndent . ' -value_template build_flags+="-Wall -Werror"',
242 '<usage>mpc:OPT=VALUE', 'Extra command-line options for MPC' .
243 $argIndent . 'For example:' .
244 $argIndent . ' --mpc:value_template build_flags+="-Wall -Werror"' .
245 $argIndent . 'turns into the following arguments for MPC:' .
246 $argIndent . ' -value_template build_flags+="-Wall -Werror"' .
247 $argIndent . 'This option can be given multiple times',
248 'boottime!', 'Use CLOCK_BOOTTIME for timers (no)',
250 ['Optional dependencies for OpenDDS (disabled by default unless noted otherwise):',
251 'java:s', 'Java development kit (use JAVA_HOME)',
252 'jboss:s', 'JBoss application server (use JBOSS_HOME)',
253 'ant:s', 'Ant for JBoss (use ANT_HOME or system pkg)',
254 'wireshark:s',
255 'Wireshark dev headers or source not built with' .
256 $argIndent . 'CMake (use WIRESHARK_SRC or system pkg)' .
257 $argIndent . 'Implies --glib',
258 'wireshark-cmake|wireshark_cmake:s',
259 'Wireshark source built with CMake, requires' .
260 $argIndent . '--wireshark-build. Requires --wireshark-lib if' .
261 $argIndent . 'guessing fails (use WIRESHARK_SRC)' .
262 $argIndent . 'Implies --glib',
263 'wireshark-build|wireshark_build=s', 'Wireshark CMake Build Location',
264 'wireshark-lib|wireshark_lib=s',
265 'Optional Wireshark CMake libraries location' .
266 $argIndent . 'relative to wireshark-build (guesses)',
267 'glib:s', 'GLib for Wireshark (use GLIB_ROOT or system pkg)',
268 '<default>rapidjson:s',
269 'RapidJSON for Wireshark dissector and JSON' .
270 $argIndent . 'Sample Serialization (Enabled by default,' .
271 $argIndent . 'use git submodule, RAPIDJSON_ROOT, or system pkg)',
272 'qt:s', 'Qt5 (use QTDIR or system pkg)',
273 'qt-include:s', 'Qt include dir (use QT5_INCDIR, QTDIR/include,' .
274 $argIndent . 'or system package)',
275 'xerces3:s', 'Xerces-C++ 3 for QoS XML handling, DDS Security',
276 'openssl:s', 'OpenSSL for DDS Security',
277 'cmake:s', 'Path to CMake for compiling GoogleTest' .
278 $argIndent . '(Check PATH and normal locations)',
279 'gtest:s', 'Path to GoogleTest, required for tests' .
280 $argIndent . '(uses GTEST_ROOT)' .
281 $argIndent . 'If not built, will try to build using CMake.',
283 ['Optional OpenDDS features:',
284 'built-in-topics!', 'Built-in Topics (yes)',
285 'content-subscription!', 'Content-Subscription Profile (yes)',
286 'content-filtered-topic!', 'ContentFilteredTopic (CS Profile) (yes)',
287 'multi-topic!', 'MultiTopic (CS Profile) (yes)',
288 'query-condition!', 'QueryCondition (CS Profile) (yes)',
289 'ownership-profile!', 'Ownership Profile (yes)',
290 'ownership-kind-exclusive!', 'Exclusive Ownership (Ownership Profile) (yes)',
291 'object-model-profile!', 'Object Model Profile (yes)',
292 'persistence-profile!', 'Persistence Profile (yes)',
293 'safety-profile:s', 'Safety Profile: base or extended (none)',
294 'tests!', 'Build tests, examples, and performance tests (no)' .
295 $argIndent . 'Requires --gtest if missing git submodule',
296 'security!', 'DDS Security plugin (no) Implies --openssl and' .
297 $argIndent . '--xerces3',
301 sub iterate {
302 my $callback = shift;
303 for my $group (@specs) {
304 for my $n (1 .. (scalar @{$group} / 2)) {
305 my $opt = ${$group}[$n * 2 - 1];
306 my $descr = ${$group}[$n * 2];
307 $opt =~ /(\<\w+\>)?([\w-]+)/;
308 my $optkey = (defined $1 ? $1 : "") . $2;
309 &$callback(${$group}[0], $opt, $descr, $optkey, @_);
314 sub usage {
315 my $status = shift;
316 my $current;
317 my $ver;
318 open VER, 'dds/Version.h' or die "ERROR: can't open dds/Version.h, stopped";
319 while (<VER>) {
320 $ver = $1 if /#define OPENDDS_VERSION "([^"]+)"/;
322 close VER;
323 print <<"EOT";
324 Welcome to OpenDDS version $ver
326 Options for this script are listed below, with the default behavior described
327 in parenthesis after the option description.
328 Boolean options can take the form "--opt" or "--no-opt", the more commonly
329 needed one (the one that changes the default behavior) is shown below.
330 Options that require arguments are shown as "--opt=VAL". Options with optional
331 arguments are shown as "--opt[=VAL]". Options that can be repeated with
332 cumulative effect are shown with a trailing "...". Some third-party
333 optional dependencies can be automatically located if they are installed in the
334 expected locations (see entries below marked with "system pkg"). In those
335 cases, specify the option as --opt without an = to enable the corresponding
336 feature in OpenDDS and use the default installation location.
338 iterate(sub {
339 my ($group, $opt, $descr, $optkey) = @_;
340 if (!defined $current || $group ne $current) {
341 $current = $group;
342 print "\n$group\n";
344 $optkey .= '=VAL' if $opt =~ /=s/;
345 $optkey .= '[=VAL]' if $opt =~ /:s/;
346 $optkey = "[no-]$optkey" if ($opt =~ /!$/ && $descr =~ / \(yes\)/);
347 if ($optkey =~ /^\<hidden\>/) {
348 return;
350 if ($opt =~ /^\<usage\>(.*)$/) {
351 $optkey = $1;
353 $optkey =~ s/^\<default\>(.*)$/\[no-\]$1/g;
354 $optkey .= '...' if $opt =~ /s\@$/;
355 my $pad = $argPadding - length $optkey;
356 print "--$optkey" . ' ' x (($pad > 0) ? $pad : 0) . " $descr\n";
359 exit $status;
362 my $defaulted = {};
364 sub parseArgs {
365 my @getopts = ();
366 my @default = ();
367 iterate(sub {
368 my ($group, $opt, $descr, $optkey) = @_;
369 if ($opt =~ /^\<usage\>/) {
370 return;
372 if ($opt =~ /^<default>(.*)$/) {
373 push @getopts, $1;
374 $optkey =~ /^<default>(.*)$/;
375 push @getopts, "no-$1";
376 push @default, $1;
378 else {
379 $opt =~ s/^\<hidden\>(.*)$/$1/;
380 push @getopts, $opt;
385 if (! -r 'rules.dds.GNU') {
386 print "ERROR: this script must be run from its own directory\n";
387 exit 1;
390 my $opts = {};
391 Getopt::Long::Configure('pass_through');
392 GetOptions($opts, @getopts) or usage(1);
393 usage(0) if $opts->{'help'};
394 targetUsage(0) if $opts->{'target-help'};
396 while (@ARGV != 0) {
397 my $arg = shift(@ARGV);
398 if ($arg =~ /^--mpc:([^=]*)(?:=(.*))?$/) {
399 my $key = $1;
400 my $value = $2;
401 if (defined($value)) {
402 push(@{$opts->{'mpcopts'}}, '-' . $key, $value);
404 elsif (@ARGV != 0) {
405 $value = shift(@ARGV);
406 push(@{$opts->{'mpcopts'}}, '-' . $key, $value);
407 } else {
408 print STDERR "ERROR: $arg requires a value\n";
409 usage(1);
411 } else {
412 print STDERR "ERROR: unknown argument $arg\n";
413 usage(1);
417 if ($opts->{'verbose'}) {
418 print "Options:\n";
419 new Dumpvalue()->dumpValue($opts);
422 for my $opt (@default) {
423 if ($opt eq 'rapidjson' && defined $opts->{'safety-profile'}) {
424 print("Although it's a default, rapidjson is not compatible with Safety ".
425 "Profile so it will not be enabled\n") if $opts->{'verbose'};
426 next;
428 if (!exists $opts->{$opt} && !exists $opts->{"no-$opt"}) {
429 $opts->{$opt} = '';
430 $defaulted->{$opt} = 1;
431 print("By default, --$opt is added to the options\n") if $opts->{'verbose'};
435 return $opts;
438 my $cross_compile = 0;
439 my %opts = %{parseArgs()};
441 my $debug = exists $opts{'debug'} ? $opts{'debug'} : 1;
442 my $backup = exists $opts{'backup'} ? $opts{'backup'} : 1;
443 my $force_ace_tao = exists $opts{'force-ace-tao'} ? $opts{'force-ace-tao'} : 0;
444 my $no_disable_deprecated = exists $opts{'no-disable-deprecated'} ? $opts{'no-disable-deprecated'} : 0;
445 my $cxx_std;
446 if (exists($opts{'std'})) {
447 $cxx_std = $opts{'std'};
448 # Accept any of --std=17, --std=stdcpp17, --std=c++17, etc.
449 $cxx_std =~ s/^(std)?(cpp|c\+\+)//;
451 my @mpcopts;
452 push(@mpcopts, @{$opts{'mpcopts'}}) if exists($opts{'mpcopts'});
453 my @features;
454 push(@features, @{$opts{'features'}}) if exists($opts{'features'});
456 $opts{'host'} = perlOS_to_host() unless $opts{'host'};
458 my $is_windows = $opts{'host'} eq 'win32';
460 my ($slash, $exeext) = $is_windows ? ('\\', '.exe') : ('/', '');
462 my %specific =
463 ($is_windows ?
464 ('ext' => 'cmd', 'pathsep' => ';', 'refpre' => '%',
465 'refpost' => '%', 'comment' => '::') :
466 ('ext' => 'sh', 'pathsep' => ':', 'refpre' => '${',
467 'refpost' => '}', 'comment' => '#')
470 sub might_be_executable {
471 my $path = shift;
472 return -x $path && -f $path; # On Windows -x can return true for directories
475 sub which {
476 my $file = shift;
477 for my $p (File::Spec->path()) {
478 next if $p eq '.';
479 my $path = "$p/$file";
480 if (might_be_executable($path)) {
481 return $path;
483 elsif ($exeext ne '') {
484 $path .= $exeext;
485 if (might_be_executable($path)) {
486 return $path;
490 return undef;
493 sub addCurLibPathRef {
494 my $buildEnvRef = shift;
495 my $platform = $opts{$buildEnvRef->{'build'}};
496 my $libpathname = $platforminfo{$platform}->{'libpath'};
497 my $curLibPathRef = $specific{'refpre'} . $libpathname . $specific{'refpost'};
498 $buildEnvRef->{$libpathname} = $curLibPathRef;
501 sub run_command {
502 my $command = shift;
503 return command_utils::run_command(
504 $command,
505 script_name => 'configure',
506 dry_run => $opts{'dry-run'},
507 verbose => $opts{'verbose'},
512 sub git_clone {
513 my $dest = shift;
514 my $url = shift;
515 my %args = @_;
516 my $branch = $args{branch};
517 my $commit = $args{commit};
519 print "Cloning git repo $url ", $branch // $commit, "\n";
521 my $failed;
522 if (defined($commit)) {
523 # Git can't directly clone a specific commit. Could use clone and checkout,
524 # but can't do a single shallow clone that way.
525 my $chdir = ChangeDir->new($dest);
526 $failed = run_command(['git', 'init', '--quiet']) ||
527 run_command(['git', 'remote', 'add', 'origin', $url]) ||
528 run_command(['git', 'fetch', '--quiet', '--depth=1', 'origin', $commit]) ||
529 run_command(['git', 'checkout', '--quiet', 'FETCH_HEAD']);
531 else {
532 my @cmd = ('git', 'clone', '--quiet', '--depth=1', $url, $dest);
533 push(@cmd, '--branch', $branch) if (defined($branch));
534 $failed = run_command(\@cmd);
537 if (!$failed) {
538 run_command(['git', '--no-pager', 'log', '-1', '--oneline'], chdir => $dest);
540 return $failed;
543 sub git_submodule_prop {
544 my $path = shift;
545 my $prop_name = shift;
546 my $full_prop_name = "submodule.$path.$prop_name";
547 open(my $fd, "-|", "git config --file .gitmodules --get $full_prop_name")
548 or die("git_submodule_prop open failed: $!\nStopped");
549 my $prop_value = <$fd>;
550 close($fd);
551 chomp($prop_value) if (defined($prop_value));
552 if (!$prop_value) {
553 die("Couldn't get $full_prop_name from .gitmodules\nStopped");
555 return $prop_value;
558 sub git_ensure_submodule {
559 my $path = shift;
561 if (!which('git')) {
562 print STDERR "Can't ensure we have $path becuase we can't find the git command\n";
563 return 0;
566 my $commit_prop = 'openddsConfigureCommit';
567 my $commit = git_submodule_prop($path, $commit_prop);
569 if (!$opts{'force-clone-submodules'} &&
570 !run_command(['git', 'submodule', 'update', '--init', $path])) {
571 # This doesn't affect us in this run, but since we can get the actual
572 # submodule commit, make sure it matches the commit from .gitmodules for
573 # the case when the source tree is not a git repo.
574 open(my $fd, "-|", "git ls-tree HEAD $path")
575 or die("git_ensure_submodule open failed: $!\nStopped");
576 my $real_commit = <$fd>;
577 close($fd);
578 chomp($real_commit);
579 $real_commit =~ s/^\S+ \S+ ([0-9a-f]+).*$/$1/;
580 if ($real_commit ne $commit) {
581 print STDERR "WARNING: for submodule $path $commit_prop = $commit ",
582 "from .gitmodules doesn't match actual commit $real_commit\n";
585 return 1;
588 return !git_clone($path, git_submodule_prop($path, 'url'), commit => $commit);
591 sub md5sum {
592 my $path = shift();
593 my $expected_hash = shift();
595 my $md5 = Digest::MD5->new;
596 open(my $fh, $path) or die("Couldn't open \"$path\": $!");
597 binmode $fh;
598 $md5->addfile($fh);
599 my $hash = $md5->hexdigest();
600 my $failed = $expected_hash ne $hash;
601 if ($failed) {
602 print(
603 "MD5 hash mismatch for $path\n",
604 " expected: $expected_hash\n",
605 " got: $hash\n");
607 return $failed;
610 my $curpathRef = $specific{'refpre'} . 'PATH' . $specific{'refpost'};
611 my %hostEnv = ('build' => 'host', 'PATH' => $curpathRef);
612 my %targetEnv = ('build' => 'target', 'PATH' => $curpathRef);
614 my $would_download; # can dry-run assume directories that don't exist yet?
616 sub locate_mpc {
617 my $ace_src = shift;
618 if (defined $opts{'mpc'}) {
619 setEnv('MPC_ROOT', $opts{'mpc'});
621 elsif (!$ENV{'MPC_ROOT'} && (-r $ace_src . '/MPC/MPC.ico'
622 || ($opts{'dry-run'} && $would_download))) {
623 setEnv('MPC_ROOT', $ace_src . $slash . 'MPC');
625 elsif (!$ENV{'MPC_ROOT'}) {
626 die "ERROR: Can't find MPC. Please set MPC_ROOT or make sure MPC exists".
627 "\n in the 'MPC' directory under ACE_ROOT ($ace_src), stopped";
629 else {
630 $targetEnv{'MPC_ROOT'} = $ENV{'MPC_ROOT'};
632 $hostEnv{'MPC_ROOT'} = $targetEnv{'MPC_ROOT'};
635 if (!exists $platforminfo{$opts{'host'}} ||
636 $platforminfo{$opts{'host'}}->{'no_host'}) {
637 die "ERROR: unknown host $opts{'host'}, stopped";
639 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";
776 if (!exists $platforminfo{'win32'}->{'cl_archs'}->{$arch}) {
777 die "cl version probe failed, invalid architecture \"$arch\"\nStopped";
779 if (!$platforminfo{'win32'}->{'cl_archs'}->{$arch}) {
780 die "ERROR: Windows for $arch isn't supported\nStopped";
782 if (!exists $platforminfo{'win32'}->{'cl_versions'}->{$ver}) {
783 die "ERROR: Unknown or unsupported Visual C++ compiler version: $ver\n"
784 . "Stopped";
787 $opts{'compiler_version'} = $platforminfo{'win32'}->{'cl_versions'}->{$ver};
788 $opts{'compiler_target_architecture'} = $arch;
790 if ($ver >= 19) {
791 push(@features, 'no_cxx11=0');
792 print "Visual C++ has >= C++11 support\n" if $opts{'verbose'};
794 if ($opts{'std'}) {
795 my $std = "stdcpp$cxx_std";
796 push(@mpcopts, '-value_template', "LanguageStandard=$std");
797 print "Setting Visual C++ LanguageStandard to $std\n" if $opts{'verbose'};
798 if ($opts{'std'} eq 'latest' || $cxx_std >= 17) {
799 push(@features, 'no_cxx17=0');
800 print "Visual C++ has >= C++17 support\n" if $opts{'verbose'};
803 $ENV{'PATH'} = $savepath;
804 print "Detected Visual C++ version: $opts{'compiler_version'}\n"
805 if $opts{'verbose'};
807 elsif ($opts{'compiler'} =~ /g\+\+|clang/) {
808 my $version_string = `$opts{'compiler'} --version`;
809 print "Compiler version: $version_string\n" if $opts{'verbose'};
810 $opts{'is apple clang'} = $version_string =~ /^Apple/;
811 if ($opts{'std'}) {
812 push(@{$opts{'macros'}}, 'CCFLAGS += -std=' . $opts{'std'});
813 print "Added platform_macros for -std=$opts{std}\n" if $opts{'verbose'};
815 elsif ($opts{'compiler'} !~ /clang/) {
816 $version_string =~ /\(.*\) (\d+)\.\d+/;
817 if ($1 >= 11) {
818 $opts{'std'} = 'gnu++17';
819 print "Detected GCC >= 11, default -std=gnu++17\n" if $opts{'verbose'};
821 elsif ($1 >= 6) {
822 $opts{'std'} = 'gnu++14';
823 print "Detected GCC >= 6, default -std=gnu++14\n" if $opts{'verbose'};
826 elsif ($opts{'compiler'} =~ /clang/ && $version_string !~ /^Apple / &&
827 $version_string =~ /(\d+)\.(\d+)\.(\d+)/) {
828 # Apple's version of Clang doesn't default to c++11 or higher, users
829 # can pass --std= to change the C++ standard version used.
830 if ($1 >= 16) {
831 $opts{'std'} = 'gnu++17';
832 print "Detected Clang >= 16, default -std=gnu++17\n" if $opts{'verbose'};
834 elsif ($1 >= 6) {
835 # Non-Apple versions of Clang, if version 6 or newer, default to C++14 like GCC
836 $opts{'std'} = 'gnu++14';
837 print "Detected Clang >= 6, default -std=gnu++14\n" if $opts{'verbose'};
841 if ($opts{'std'} && $opts{'std'} =~ /(0x|11|1y|14|1z|17|2a|20|2b|23)$/) {
842 push(@features, 'no_cxx11=0');
843 print "Compiler has >= C++11 support\n" if $opts{'verbose'};
848 my $compile_warnings = $opts{'compile-warnings'} // '';
849 if ($compile_warnings eq 'WARNING' or $compile_warnings eq 'ERROR') {
850 my ($section_names, $sections) = read_ini_file("$FindBin::RealBin/build.ini");
851 my %compiler_map = ( 'g++' => 'GNU', 'clang' => 'Clang', 'cl' => 'MSVC', 'cl.exe' => 'MSVC');
852 my $section = $compiler_map{$opts{'compiler'}};
853 if ($opts{'is apple clang'}) {
854 $section = 'AppleClang';
856 my $warning_flags = $sections->{$section}{'warning'};
857 my $error_flags = $sections->{$section}{'error'};
858 push(@mpcopts, '-value_template', "compile_flags+=${warning_flags}");
859 if ($compile_warnings eq 'ERROR') {
860 push(@mpcopts, '-value_template', "compile_flags+=${error_flags}");
864 sub looksRelative {
865 my $val = shift;
866 return substr($val, 0, 1) ne $slash && ($slash eq '/' || $val !~ /^[a-z]:/i);
869 sub normalizePath {
870 my $val = shift;
871 return Cwd::abs_path($val) if $val && -d $val && $val =~ /../;
872 return $val;
875 sub setSomeEnv {
876 my($hashref, $name, $val, $notdir) = @_;
877 $val = Cwd::abs_path($val) if -d $val;
878 if ($opts{'dry-run'} && !$notdir && looksRelative($val)) {
879 $val = getcwd . $slash . $val;
881 $val =~ s!/!\\!g if $is_windows;
882 $hashref->{$name} = $val;
885 sub setEnv {
886 setSomeEnv(\%targetEnv, @_);
889 sub setHostEnv {
890 setSomeEnv(\%hostEnv, @_);
893 ## ace
894 my $ace_src;
896 if ($opts{'ace'}) {
897 if ($opts{'ace'} ne 'download') {
898 if (!-r $opts{'ace'} . '/ace/ACE.h') {
899 die "ERROR: Can't find ACE at $opts{'ace'}.\nStopped";
901 $ace_src = $opts{'ace'};
904 elsif ($ENV{'ACE_ROOT'}) {
905 if (!-r $ENV{'ACE_ROOT'} . '/ace/ACE.h') {
906 die "ERROR: Can't find ACE at $ENV{'ACE_ROOT'}.\nStopped";
908 $ace_src = $ENV{'ACE_ROOT'};
910 elsif (-r '../ACE_wrappers/ace/ACE.h') {
911 die "ERROR: Older versions of this script would default to using ACE at " .
912 "../ACE_wrappers, but this version doesn't. Use the --ace command line " .
913 "option to override this error. Use --ace=download to have this script " .
914 "download an ACE+TAO package and expand it to ACE_wrappers.\nStopped";
916 elsif (-r 'ACE_wrappers/ace/ACE.h') {
917 $ace_src = 'ACE_wrappers';
919 elsif (-r 'ATCD/ACE/ace/ACE.h') {
920 $ace_src = 'ATCD/ACE';
922 elsif (-r 'ACE_TAO/ACE/ace/ACE.h') {
923 $ace_src = 'ACE_TAO/ACE';
926 $ace_src = normalizePath($ace_src);
928 ## tao
929 my $tao_src;
931 if ($opts{'tao'}) {
932 if (!-r $opts{'tao'} . '/tao/ORB.h') {
933 die "ERROR: Can't find TAO at $opts{'tao'}.\nStopped";
935 $tao_src = $opts{'tao'};
937 elsif ($ENV{'TAO_ROOT'}) {
938 if (!-r $ENV{'TAO_ROOT'} . '/tao/ORB.h') {
939 die "ERROR: Can't find TAO at $ENV{'TAO_ROOT'}.\nStopped";
941 $tao_src = $ENV{'TAO_ROOT'};
943 elsif (defined $ace_src && -r $ace_src . '/TAO/tao/ORB.h') {
944 $tao_src = $ace_src . $slash . 'TAO';
946 elsif (defined $ace_src && -r $ace_src . '/../TAO/tao/ORB.h') {
947 $tao_src = (File::Spec->splitpath($ace_src))[1] .'TAO';
950 $tao_src = normalizePath($tao_src);
952 if ($opts{'safety-profile'}) {
953 # convert to lower case
954 $opts{'safety-profile'} = lc($opts{'safety-profile'});
957 ## Download ACE+TAO
958 if (!$ace_src || !$tao_src) {
959 if ($opts{'ace-github-latest'}) {
960 die "ERROR: Git not found in path (required to clone ACE/TAO/MPC)"
961 if ! which('git');
963 my $urlbase = 'https://github.com/DOCGroup';
964 my $branch = 'ace6tao2';
965 if ($opts{'doc-group3'}) {
966 $branch = 'master';
969 my $err = git_clone('ACE_TAO', "$urlbase/ACE_TAO", branch => $branch);
970 die "ERROR: Failed to clone ACE/TAO from GitHub\nStopped"
971 if $err ||
972 ! -r 'ACE_TAO/ACE/ace/ACE.h' ||
973 ! -r 'ACE_TAO/TAO/tao/ORB.h';
975 $err = git_clone('ACE_TAO/ACE/MPC', "$urlbase/MPC", branch => 'master');
976 die "ERROR: Failed to clone MPC (into ACE_TAO/ACE/MPC) from GitHub\nStopped"
977 if $err ||
978 ! -r 'ACE_TAO/ACE/MPC/mwc.pl';
980 $ace_src = normalizePath('ACE_TAO/ACE');
981 $tao_src = normalizePath('ACE_TAO/TAO');
983 else {
984 # Get ACE/TAO version info
985 my ($section_names, $sections) = read_ini_file("$FindBin::RealBin/acetao.ini");
986 my $ace_tao_version = $opts{'doc-group3'} ? $sections->{ace7tao3} : $sections->{ace6tao2};
987 if ($opts{verbose}) {
988 print("ACE/TAO Version Info:");
989 new Dumpvalue()->dumpValue($ace_tao_version);
991 my $ext = $is_windows ? 'zip' : 'tar.gz';
992 my $file = $ace_tao_version->{"$ext-filename"};
993 my $url = $ace_tao_version->{"$ext-url"};
994 my $md5_hash = $ace_tao_version->{"$ext-md5"};
996 # Check for an existing file
997 if (-r $file) {
998 if (md5sum($file, $md5_hash)) {
999 if ($opts{'dry-run'}) {
1000 print("Would remove existing $file and attempt to download\n");
1002 else {
1003 print("Removing existing $file and attempting to download\n");
1004 unlink($file) or die("Couldn't remove $file: $!\nStopped");
1007 elsif ($opts{'verbose'}) {
1008 print("Using ACE+TAO source package $file\n");
1012 if (!-r $file) {
1013 my $dl_msg = "Downloading $file from $url using";
1014 $would_download = 1;
1015 eval {
1016 require LWP::UserAgent;
1017 my $ua = LWP::UserAgent->new;
1018 $ua->env_proxy;
1019 print("$dl_msg LWP\n");
1020 if ($opts{'dry-run'}) {
1021 print("Dry-run: would LWP::UserAgent get $url\n");
1023 else {
1024 my $response = $ua->get($url, ':content_file' => $file);
1025 if ($response->is_error) {
1026 die $response->message . "\nstopped";
1030 if ($@) {
1031 if (which('wget')) {
1032 print("$dl_msg wget\n");
1033 run_command(['wget', '--output-document', $file, $url], autodie => 1);
1035 elsif (which('curl')) {
1036 print("$dl_msg curl\n");
1037 run_command(['curl', '--location', $url, '--output', $file], autodie => 1);
1039 else {
1040 die "ERROR: Can't download ACE+TAO using LWP, wget, or curl.\n" .
1041 "Download ACE+TAO from $url, place the file here\n, " .
1042 "and re-run the script.\nStopped";
1047 if (!$opts{'dry-run'} && md5sum($file, $md5_hash)) {
1048 die("MD5 hash check failed after download, try running again?\nStopped");
1051 print "Extracting archive $file\n";
1052 $ENV{'ACTIVEPERL_CONFIG_DISABLE'} = 1 if $^O eq 'MSWin32';
1053 $ENV{'ACTIVEPERL_CONFIG_SILENT'} = 1 if $^O eq 'MSWin32';
1054 eval {require Archive::Extract;};
1055 if ($@) {
1056 my $err = 1;
1057 my $ddsroot = getcwd;
1058 if (!$is_windows) {
1059 $err = run_command(['tar', 'xzf', "$ddsroot/$file"]);
1061 else {
1062 # Try Archive::Zip
1063 print "Archive::Extract isn't installed, trying Archive::Zip\n" if $opts{'verbose'};
1064 eval {require Archive::Zip};
1065 if ($@) {
1066 print "Neither Archive::Extract or Archive::Zip are installed\n" if $opts{'verbose'};
1068 else {
1069 if ($opts{'dry-run'}) {
1070 print "Dry-run: would Archive::Zip $file\n";
1072 else {
1073 my $zip = Archive::Zip->new();
1074 if ($zip->read( $file ) == Archive::Zip::AZ_OK() &&
1075 $zip->extractTree() == Archive::Zip::AZ_OK()) {
1076 $err = 0;
1082 if ($err) {
1083 die "ERROR: Can't extract $file, extract it to " . Cwd::abs_path('.') .
1084 "\nand run this script again.\nStopped";
1087 else {
1088 if ($opts{'dry-run'}) {
1089 print "Dry-run: would Archive::Extract $file\n";
1091 else {
1092 if ($^O ne 'MSWin32') {
1093 no warnings 'once';
1094 $Archive::Extract::PREFER_BIN = 1;
1096 my $ae = Archive::Extract->new('archive' => $file);
1097 if (!$ae->extract('to' => '.')) {
1098 die $ae->error . "\nstopped";
1103 unlink $file;
1104 print "Removed $file\n" if $opts{'verbose'};
1105 $ace_src = 'ACE_wrappers';
1106 $tao_src = 'ACE_wrappers/TAO';
1110 print "Using ace_src: $ace_src\n" if $opts{'verbose'};
1111 print "Using tao_src: $tao_src\n" if $opts{'verbose'};
1113 sub clone_host_and_target {
1114 my $source_dir = shift;
1115 locate_mpc($ace_src);
1116 print "cloning build tree\n" if $opts{'verbose'};
1117 if (run_command(
1118 ["$targetEnv{'MPC_ROOT'}/clone_build_tree.pl", 'host', 'target'],
1119 chdir => $source_dir)) {
1120 die("Failed to clone tree");
1124 sub backup {
1125 my $file = shift;
1126 if (!$opts{'dry-run'} && -r $file) {
1127 print "WARNING: overwriting existing $file\n";
1128 if ($backup) {
1129 my $backup_path = $file . '.bak';
1130 $backup_path .= ".$backup_timestamp" if -e $backup_path;
1131 copy($file, $backup_path);
1132 print " (saved a backup copy as $backup_path)\n";
1134 unlink $file;
1138 sub backup_and_copy {
1139 my $src = shift;
1140 my $dst = shift;
1141 backup($dst);
1142 if ($opts{'dry-run'}) {
1143 print("Would copy $src to $dst\n");
1145 else {
1146 copy($src, $dst);
1150 sub backup_and_open {
1151 my $file = shift;
1152 backup($file);
1153 if ($opts{'dry-run'}) {
1154 return File::Temp->new();
1156 my $fh = new FileHandle;
1157 open $fh, ">$file" or die "ERROR: Can't write to $file, stopped";
1158 return $fh;
1161 sub dump_and_unlink { # removes temp files created by dry-run
1162 my $tfile = shift;
1163 if ($opts{'verbose'}) {
1164 open TMP, $tfile;
1165 print <TMP>;
1166 close TMP;
1168 unlink $tfile;
1171 sub write_config_h {
1172 my %buildEnv = %{shift()};
1173 my $platform = $opts{$buildEnv{'build'}};
1174 my $pi = $platforminfo{$platform};
1175 $opts{'optimize'} = 0 if (!exists $opts{'optimize'} && !exists $opts{'sanitize'});
1177 my $CFGH = backup_and_open("$buildEnv{'ACE_ROOT'}/ace/config.h");
1178 if (!$no_disable_deprecated) {
1179 for my $line (@default_configh) {
1180 print $CFGH "$line\n";
1183 if ($buildEnv{'build'} eq 'target') {
1184 for my $line (@{$opts{'configh'}}) {
1185 print $CFGH "$line\n";
1188 my $cfg = $platform;
1189 if ($pi->{'aceconfig'}) {
1190 $cfg = $pi->{'aceconfig'};
1191 $cfg =~ s/\$UNAMER/my $u = `uname -r`; chomp $u; $u/e;
1193 $cfg .= '-' . $opts{'host_version'} if $opts{'host_version'};
1194 print $CFGH "#include \"ace/config-$cfg.h\"\n";
1195 if (defined $opts{'no-opendds-safety-profile'}) {
1196 print $CFGH "#define ACE_FACE_SAFETY_" . uc($opts{'safety-profile'}) . "\n";
1197 if ($opts{'safety-profile'} eq 'extended') {
1198 print $CFGH "#ifndef ACE_HAS_ALLOC_HOOKS\n";
1199 print $CFGH "# define ACE_HAS_ALLOC_HOOKS\n";
1200 print $CFGH "#endif\n";
1204 close $CFGH;
1205 print "Wrote $buildEnv{'ACE_ROOT'}/ace/config.h\n" if $opts{'verbose'};
1206 dump_and_unlink($CFGH) if $opts{'dry-run'};
1209 my $wrote_df = 0;
1211 sub default_features {
1212 my %buildEnv = %{shift()};
1213 my @feat;
1214 if ($buildEnv{'build'} eq 'target') {
1215 push(@feat, 'ipv6=1') if $opts{'ipv6'};
1216 my @normalized = map {/=/ ? $_ : "$_=1"} @features;
1217 push(@feat, @normalized) if @normalized;
1219 return @feat;
1222 sub write_default_features {
1223 my %buildEnv = %{shift()};
1224 my @feat = default_features(\%buildEnv);
1226 if (@feat) {
1227 my $DF = backup_and_open("$buildEnv{'ACE_ROOT'}/bin/MakeProjectCreator" .
1228 "/config/default.features");
1229 $wrote_df = 1;
1230 for my $f (@feat) {
1231 print $DF "$f\n";
1233 $DF->close;
1234 print "Wrote $buildEnv{'ACE_ROOT'}/.../default.features\n"
1235 if $opts{'verbose'};
1236 dump_and_unlink($DF) if $opts{'dry-run'};
1240 my %all_sanitizers = (
1241 asan => {
1242 fsanitize => 'address',
1243 env => {
1244 LSAN_OPTIONS => "suppressions=$FindBin::RealBin/etc/asan-suppr.txt",
1245 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',
1247 compiler_args => ['-fsanitize-address-use-after-scope'],
1249 tsan => {
1250 fsanitize => 'thread',
1251 env => {
1252 TSAN_OPTIONS => "history_size=7 second_deadlock_stack=1 suppressions=$FindBin::RealBin/etc/tsan-suppr.txt",
1255 ubsan => {
1256 fsanitize => 'undefined',
1257 env => {
1258 UBSAN_OPTIONS => "suppressions=$FindBin::RealBin/etc/ubsan-suppr.txt:print_stacktrace=1",
1260 configh => ['#define ACE_INITIALIZE_MEMORY_BEFORE_USE'],
1261 compiler_args => ['-fno-sanitize=enum'],
1264 my %enabled_sanitizers = ();
1265 if (exists $opts{'sanitize'}) {
1266 for my $arg (@{$opts{'sanitize'}}) {
1267 for my $name (split(/,/, $arg)) {
1268 if (exists($all_sanitizers{$name})) {
1269 $enabled_sanitizers{$name} = $all_sanitizers{$name};
1271 else {
1272 die("ERROR: \"$name\" isn't a valid sanitizer to pass to --sanitize\nStopped");
1278 my @fsanitize = ();
1279 my @sanitizer_compiler_args = ();
1280 my @sanitizer_linker_args = ();
1281 for my $name (sort(keys(%enabled_sanitizers))) {
1282 my $sanitizer = $enabled_sanitizers{$name};
1283 push(@fsanitize, $sanitizer->{fsanitize});
1284 if (exists($sanitizer->{env})) {
1285 for my $env (keys(%{$sanitizer->{env}})) {
1286 setEnv($env, $sanitizer->{env}->{$env});
1289 if (exists($sanitizer->{configh})) {
1290 push(@{$opts{'configh'}}, @{$sanitizer->{configh}});
1292 if (exists($sanitizer->{compiler_args})) {
1293 push(@sanitizer_compiler_args, @{$sanitizer->{compiler_args}});
1295 if (exists($sanitizer->{linker_args})) {
1296 push(@sanitizer_linker_args, @{$sanitizer->{linker_args}});
1299 if (scalar(@fsanitize)) {
1300 if (!$debug) {
1301 die("ERROR: Using sanitizers requires --debug");
1303 my @common = ('-ggdb', '-fsanitize=' . join(',', @fsanitize));
1304 @sanitizer_compiler_args = ('-O1', @common, '-fno-omit-frame-pointer', @sanitizer_compiler_args);
1305 @sanitizer_linker_args = (@common, @sanitizer_linker_args);
1308 sub compiler {
1309 my %buildEnv = %{shift()};
1310 return ($buildEnv{'build'} eq 'target' && $opts{'target-compiler'}) ?
1311 $opts{'target-compiler'} : $opts{'compiler'};
1314 my @ace_macros = ('debug',
1315 'optimize',
1316 'inline',
1317 'static',
1318 'ipv6');
1320 my @platformmacros;
1321 sub write_platform_macros {
1322 my %buildEnv = %{shift()};
1323 if (!$is_windows ||
1324 ($cross_compile && $buildEnv{'build'} eq 'target')) {
1325 my $pi = $platforminfo{$opts{$buildEnv{'build'}}};
1326 my $PMG = backup_and_open("$buildEnv{'ACE_ROOT'}/include/makeinclude" .
1327 "/platform_macros.GNU");
1328 my $macro_cross_compile = 0;
1329 if ($buildEnv{'build'} eq 'target') {
1330 for my $line (@{$opts{'macros'}}) {
1331 print $PMG "$line\n";
1333 for my $key (@ace_macros) {
1334 if (exists $opts{$key}) {
1335 my $macro = ($key eq 'static') ? 'static_libs_only' : $key;
1336 print $PMG "$macro = $opts{$key}\n";
1339 if ($cross_compile) {
1340 print $PMG "CROSS-COMPILE = 1\n";
1341 print $PMG 'TAO_IDL = $(HOST_ACE)/bin/tao_idl', "\n";
1342 if ($is_windows) {
1343 print $PMG "HOST_EXE_EXT = .exe\n";
1345 print $PMG 'TAO_IDL_DEP = $(TAO_IDL)$(HOST_EXE_EXT)', "\n";
1346 if ($has_host_compiler) {
1347 print $PMG 'TAO_IDL_PREPROCESSOR = ', $opts{'compiler'}, "\n";
1349 print $PMG 'TAO_IDLFLAGS += -g $(HOST_ACE)/bin/ace_gperf', "\n";
1350 print $PMG 'build_tao_idl_be = 0', "\n";
1351 unless ($build_host_tools) {
1352 my $host_ace;
1353 if (exists $opts{'host-ace'}) {
1354 $host_ace = $opts{'host-ace'};
1356 else {
1357 $host_ace = '$(HOST_DDS)' . nested($ace_src, '.');
1359 print $PMG "HOST_ACE = $host_ace\n";
1361 if ($opts{'target-compiler'}) {
1362 my $tcomp = $opts{'target-compiler'};
1363 if ($tcomp =~ s/-g([c+])\1$/-/) {
1364 $macro_cross_compile = 1;
1365 print $PMG 'CROSS_COMPILE = ', $tcomp, "\n";
1367 else {
1368 $opts{'nonstdcompiler'} = $tcomp;
1369 print $PMG 'LDFLAGS += -Wl,-rpath-link,$(ACE_ROOT)/lib', "\n";
1372 if ($opts{'target-arch'}) {
1373 print $PMG "FLAGS_C_CC += -target $opts{'target-arch'}\n";
1374 print $PMG "LDFLAGS += -target $opts{'target-arch'}\n";
1376 if ($opts{'target'} eq 'android') {
1377 print $PMG 'ifeq (,$(findstring -isystem$(ACE_ROOT),$(INCLDIRS)))', "\n";
1378 print $PMG ' INCLDIRS += -isystem $(ACE_ROOT)', "\n";
1379 print $PMG 'endif', "\n";
1383 for my $f (@platformmacros) {
1384 print $PMG ($f =~ /=/ ? $f : "$f=1"), "\n";
1386 if ($buildEnv{'build'} eq 'host') {
1387 print $PMG "static_libs_only = 1\n";
1388 print $PMG "java = 1\n" if $opts{'java'};
1390 if ($opts{'prefix'}) {
1391 print $PMG "INSTALL_PREFIX=" . $opts{'prefix'} . "\n";
1393 if (scalar(@fsanitize)) {
1394 print $PMG
1395 'CPPFLAGS += ' . join(' ', @sanitizer_compiler_args) . "\n" .
1396 'LDFLAGS += ' . join(' ', @sanitizer_linker_args) . "\n";
1398 my $plat = $opts{$buildEnv{'build'}};
1399 if ($pi->{'aceplatform'}) {
1400 $plat = $pi->{'aceplatform'};
1401 $plat =~ s/\$COMP/'g++'/e;
1402 $plat =~ s/\$NONSTDCOMP/($opts{'compiler'} =~ m!clang!) ? 'clang' : ''/e;
1403 $plat =~ s/_$//;
1405 $plat .= '_' . $opts{'host_version'} if $opts{'host_version'};
1406 print $PMG "include \$(ACE_ROOT)/include/makeinclude/platform_$plat.GNU\n";
1407 if ($opts{'nonstdcompiler'} && !$macro_cross_compile) {
1408 my $comp = compiler(\%buildEnv);
1409 for my $var ('CC', 'CXX', 'LD') {
1410 print $PMG "$var = $comp\n";
1413 if ($opts{'prefix'} && $opts{'install-origin-relative'}) {
1414 print $PMG <<'EOT';
1415 install_rpath = 0
1416 INSTALL_ORIGIN = $$ORIGIN/../$(INSTALL_LIB)
1417 LDFLAGS += '-Wl,-rpath,$(INSTALL_ORIGIN)' $(LD_RPATH_FLAGS)
1420 $PMG->close;
1421 print "Wrote $buildEnv{'ACE_ROOT'}/.../platform_macros.GNU\n"
1422 if $opts{'verbose'};
1423 dump_and_unlink($PMG) if $opts{'dry-run'};
1427 sub write_opendds_configh {
1428 my %buildEnv = %{shift()};
1429 my $CFGH = backup_and_open("$buildEnv{'DDS_ROOT'}/dds/OpenDDSConfig.h");
1430 my $CFGIN = new FileHandle;
1431 open($CFGIN, 'dds/OpenDDSConfig.h.in') or die("Can't open OpenDDSConfig.h.in for reading: $!\nStopped");
1433 my %config = (
1434 'OPENDDS_CONFIG_AUTO_STATIC_INCLUDES' => 0,
1435 'OPENDDS_CONFIG_BOOTTIME_TIMERS' => $opts{'boottime'} // 0,
1438 my $replace_value = sub {
1439 my $var = shift;
1440 return 'UNDEFINED_NEED_TO_UPDATE_CONFIGURE_SCRIPT' unless exists $config{$var};
1441 return $config{$var};
1444 while (<$CFGIN>) {
1445 s/@(\w+)@/&$replace_value($1)/e;
1446 print $CFGH $_;
1449 $CFGIN->close;
1450 $CFGH->close;
1451 print "Wrote $buildEnv{'DDS_ROOT'}/dds/OpenDDSConfig.h\n"
1452 if $opts{'verbose'};
1453 dump_and_unlink($CFGH) if $opts{'dry-run'};
1456 ## Optional OpenDDS dependencies
1457 my %optdep = (
1458 'java' => {env => 'JAVA_HOME', sanity => 'include/jni.h', mpc => 'java'},
1459 'jboss' => {env => 'JBOSS_HOME', sanity => 'lib/jboss-common.jar'},
1460 'ant' => {env => 'ANT_HOME', sanity => 'bin/ant'},
1461 'wireshark' => {env => 'WIRESHARK_SRC', sanity => 'epan/packet.h', mpc => 'wireshark'},
1462 'wireshark-cmake' => {
1463 env => 'WIRESHARK_SRC',
1464 sanity => 'epan/packet.h',
1465 mpc => 'wireshark_cmake',
1467 'wireshark-build' => {env => 'WIRESHARK_BUILD', sanity => 'config.h'},
1468 'wireshark-lib' => {env => 'WIRESHARK_LIB', may_be_blank => 1},
1469 'glib' => {
1470 env => 'GLIB_ROOT',
1471 sanity => {
1472 'include/glib-2.0/glib.h' => undef,
1473 'include/glib.h' => 'glib_versioned_includes=0',
1476 'rapidjson' => {
1477 env => 'RAPIDJSON_ROOT',
1478 sanity => 'include/rapidjson/rapidjson.h',
1479 mpc => 'no_rapidjson=0',
1481 'qt' => {env => 'QTDIR', sanity => '', mpc => 'qt5'},
1482 'xerces3' => {
1483 env => 'XERCESCROOT',
1484 sanity => 'include/xercesc/dom/DOM.hpp',
1485 mpc => 'xerces3',
1487 'openssl' => {env => 'SSL_ROOT', sanity => 'include/openssl/opensslv.h', mpc => 'ssl'},
1490 my $host_tools_only = exists $opts{'host-tools-only'} && $opts{'host-tools-only'};
1491 if ($host_tools_only) {
1492 print "--host-tools-only implies --static\n" if $opts{'verbose'};
1493 $opts{'static'} = 1;
1494 if ($cross_compile) {
1495 die "ERROR: Can't use --host-tools-only for cross compile\nStopped";
1499 if ((exists $opts{'wireshark'} || exists $opts{'wireshark-cmake'}) &&
1500 !exists $opts{'glib'}) {
1501 print "--wireshark and --wireshark-cmake imply --glib\n" if $opts{'verbose'};
1502 $opts{'glib'} = '';
1505 # Use this to check if tests are enabled (instead of $opts{'tests'} directly).
1506 my $tests = exists $opts{'tests'} && $opts{'tests'};
1508 my @ace_features = ('xerces3');
1510 if (exists $opts{'java'}) {
1511 if ($opts{'static'} && !$host_tools_only) {
1512 die "ERROR: --static can't be used with --java\nStopped";
1514 my $host_java_platform = perlOS_to_java_platform();
1515 if ($cross_compile) {
1516 if (exists $platforminfo{$opts{'target'}}->{'java_platform'}) {
1517 setEnv('JAVA_PLATFORM', $platforminfo{$opts{'target'}}->{'java_platform'});
1518 setHostEnv('JAVA_PLATFORM', $host_java_platform);
1520 else {
1521 die "Cross-compile with Java for $opts{'target'} in not supported.\nStopped";
1524 else {
1525 setEnv('JAVA_PLATFORM', $host_java_platform);
1529 my $try_to_use_qt_system_pkg =
1530 exists $opts{'qt'} && !length($opts{'qt'}) && !exists $opts{'qt-include'};
1532 # Default to Wireshark Development Package if installed and a path wasn't
1533 # supplied.
1534 my $wireshark_install = '/usr/include/wireshark';
1535 if (exists $opts{'wireshark'} && !defined $ENV{'WIRESHARK_SRC'} && $opts{'wireshark'} eq '') {
1536 my $sanity = $optdep{'wireshark'}->{sanity};
1537 if (-f File::Spec->catdir($wireshark_install, $sanity)) {
1538 $opts{'wireshark'} = $wireshark_install;
1540 else {
1541 die "ERROR: --wireshark must be given a value because there is not a " .
1542 "development package installed at " . $wireshark_install . ", stopped";
1546 my $wireshark_lib_defaulted = 0;
1547 if (exists $opts{'wireshark-cmake'} && !exists $opts{'wireshark-lib'}) {
1548 if ($^O =~ /MSWin32/) {
1549 $opts{'wireshark-lib'} = "run\\RelWithDebInfo";
1551 elsif ($^O =~ /darwin/) {
1552 $opts{'wireshark-lib'} = "run/Wireshark.app/Contents/Frameworks";
1554 elsif ($^O =~ /linux/) {
1555 $opts{'wireshark-lib'} = "";
1557 else {
1558 die "ERROR: --wireshark-lib is needed because we couldn't decide on a default value";
1560 $wireshark_lib_defaulted = 1;
1563 if ($opts{'security'}) {
1564 unless (exists $opts{'openssl'}) {
1565 $opts{'openssl'} = '';
1566 print "Forcing --openssl (security dependency)\n" if $opts{'verbose'};
1568 unless (exists $opts{'xerces3'}) {
1569 $opts{'xerces3'} = '';
1570 print "Forcing --xerces3 (security dependency)\n" if $opts{'verbose'};
1574 # Try to find CMake
1575 unless ($opts{'cmake'}) {
1576 print "--cmake not specified by user; searching path...\n" if $opts{'verbose'};
1577 $opts{'cmake'} = which('cmake');
1579 unless ($opts{'cmake'}) {
1580 print "CMake not found in path; searching default location...\n"
1581 if $opts{'verbose'};
1583 $opts{'cmake'} =
1584 ($is_windows ?
1585 (-f $ENV{'ProgramFiles'} . '\\CMake\\bin\\cmake.exe' ?
1586 $ENV{'ProgramFiles'} . '\\CMake\\bin\\cmake.exe' :
1587 $ENV{'ProgramFiles(x86)'} . '\\CMake\\bin\\cmake.exe') :
1588 '/usr/bin/cmake');
1591 my $has_cmake = -f $opts{'cmake'};
1592 if ($has_cmake) {
1593 print "Using CMake '$opts{'cmake'}' if needed\n" if $opts{'verbose'};
1595 else {
1596 print "Could not find CMake at '$opts{'cmake'}'\n" if $opts{'verbose'};
1599 sub targets_win64 {
1600 my $arch = exists $opts{'compiler_target_architecture'} ?
1601 $opts{'compiler_target_architecture'} : '';
1603 return $arch eq 'x64';
1606 sub system_default_install_dir {
1607 if ($is_windows) {
1608 # When the host is 32-bit.
1609 return $ENV{'ProgramFiles'}
1610 if ! exists $ENV{'ProgramFiles(x86)'};
1612 return targets_win64() ? $ENV{'ProgramFiles'} : $ENV{'ProgramFiles(x86)'};
1614 return '/usr';
1617 my $build_gtest = 0;
1618 if (exists $opts{'gtest'} || $tests) {
1619 my $gtest_sanity = 'include/gtest/gtest.h';
1620 my $gtest_root = undef;
1622 if ($opts{'gtest'}) {
1623 die "ERROR: '$gtest_sanity' not found in supplied gtest directory '$opts{'gtest'}'"
1624 if ! -f File::Spec->catfile($opts{'gtest'}, $gtest_sanity);
1626 $gtest_root = $opts{'gtest'};
1627 setEnv('GTEST_ROOT', $gtest_root);
1630 else {
1631 my $rel_path = 'tests/googletest';
1632 my $sm_dir = Cwd::abs_path($rel_path);
1633 my $sm_src = File::Spec->catdir($sm_dir, 'googletest');
1634 my $sys_dir = File::Spec->catdir((system_default_install_dir(),
1635 $is_windows ? 'googletest-distribution' : ''));
1637 my $use_sm_dir = 0;
1638 if (-f File::Spec->catfile($sm_src, $gtest_sanity)) {
1639 $use_sm_dir = 1;
1641 elsif (-f File::Spec->catfile($sys_dir, $gtest_sanity)) {
1642 $opts{'gtest'} = $sys_dir;
1643 $gtest_root = $sys_dir;
1644 setEnv('GTEST_ROOT', $gtest_root);
1646 else {
1647 print "Could not find GoogleTest, cloning...\n" if $opts{'verbose'};
1648 if (git_ensure_submodule($rel_path)) {
1649 $use_sm_dir = 1;
1653 if ($use_sm_dir) {
1654 # Check for existing build
1655 $gtest_root = File::Spec->catdir($sm_dir, 'build', 'install');
1656 $build_gtest = ! -d $gtest_root;
1657 $opts{'gtest'} = $sm_dir;
1658 # No need to set env. variable with submodule UNLESS we're in safetyprofile
1659 if ($opts{'safety-profile'}) {
1660 print "Setting GTEST_ROOT to ${gtest_root} for use in safety-profile.\n" if $opts{'verbose'};
1661 setEnv('GTEST_ROOT', $gtest_root);
1665 if (!defined($gtest_root)) {
1666 die "ERROR: GoogleTest '$gtest_sanity' not found in submodule src '$sm_src' or " .
1667 "default install dir '$sys_dir', please pass a correct version of GoogleTest to --gtest\nStopped";
1670 if ($is_windows && -d File::Spec->catdir($gtest_root, 'bin')) {
1671 push_path(\%targetEnv, $gtest_root . $slash . 'bin');
1673 elsif (!$is_windows && -f File::Spec->catfile($gtest_root, 'lib', 'libgtest.so')) {
1674 push_libpath(\%targetEnv, $gtest_root . $slash . 'lib');
1678 if ($build_gtest && !$has_cmake) {
1679 die "ERROR: GoogleTest in $opts{'gtest'} must be built but can't find CMake\nStopped";
1682 if (exists $opts{'rapidjson'}) {
1683 if ($opts{'rapidjson'} eq '') {
1684 my $sanity = $optdep{'rapidjson'}->{sanity};
1685 my $rel_path = 'tools/rapidjson';
1686 my $sm_dir = Cwd::abs_path($rel_path);
1687 my $sys_dir = File::Spec->catdir((system_default_install_dir(),
1688 $is_windows ? 'RapidJSON' : ''));
1690 my $rapidjson_msg =
1691 "Could not find RapidJSON (using '$sanity') in:\n".
1692 " Git Submodule: '$sm_dir'\n" .
1693 " Default Install Prefix: '$sys_dir'\n";
1694 my $rapidjson = undef;
1695 if (-f File::Spec->catfile($sm_dir, $sanity)) {
1696 $rapidjson = $sm_dir;
1698 elsif (-f File::Spec->catfile($sys_dir, $sanity)) {
1699 $rapidjson = $sys_dir;
1701 else {
1702 print "Could not find RapidJSON, cloning...\n" if $opts{'verbose'};
1703 if (git_ensure_submodule($rel_path)) {
1704 $rapidjson = $sm_dir;
1708 if (defined($rapidjson)) {
1709 print "Using '$rapidjson' for RapidJSON\n" if ($opts{'verbose'});
1710 $opts{'rapidjson'} = $rapidjson;
1712 elsif (exists($defaulted->{'rapidjson'})) {
1713 print "${rapidjson_msg}Continuing without it.\n" if $opts{'verbose'};
1714 delete $opts{'rapidjson'};
1716 else {
1717 die "ERROR: ${rapidjson_msg}Stopped";
1722 my %use_system_pkg = map {$_, 1} qw/ant glib qt xerces3 openssl/;
1724 my %use_win_default = (
1725 'openssl' => system_default_install_dir() . '/OpenSSL',
1726 'xerces3' => system_default_install_dir() . '/xerces-c');
1728 my %need_platform_macros = map {$_, 1} qw/xerces3/;
1730 sub default_java_home {
1731 my $host = $opts{'host'};
1732 my $result = '';
1733 if ($host eq 'macosx') {
1734 $result = `/usr/libexec/java_home`;
1735 chomp $result;
1736 return $result;
1738 else {
1739 $result = which('javac');
1740 if (!$result) {
1741 die "ERROR: No javac in PATH.\nStopped";
1743 while (-l $result) {
1744 $result = readlink($result);
1746 # remove bin/javac
1747 return dirname(dirname($result))
1751 # Detect and populate dependency info from environment variables
1752 # or system-wide default paths.
1753 for my $key (keys %optdep) {
1754 if (exists $opts{$key} && $opts{$key} eq '') {
1755 if (defined $optdep{$key}->{env} && $ENV{$optdep{$key}->{env}}) {
1756 $opts{$key} = $ENV{$optdep{$key}->{env}};
1758 if ($key eq 'java') {
1759 ## when the environment variable JAVA_HOME is set to a JRE location,
1760 ## try to resolve JAVA_HOME based on the location of javac
1761 my $java_home = default_java_home();
1762 if (!-r "$opts{$key}$slash$optdep{'java'}->{sanity}" and $java_home ne '') {
1763 $opts{'java'} = $java_home;
1767 elsif (!$is_windows && exists $use_system_pkg{$key}) {
1768 $opts{$key} = '/usr';
1769 print "Defaulting $key to $opts{$key}\n" if $opts{'verbose'};
1771 elsif ($is_windows && exists $use_win_default{$key}) {
1772 my $dir = $use_win_default{$key};
1774 if (-d $dir) {
1775 $opts{$key} = $dir;
1777 else {
1778 die "Default '$key' directory '$dir' not found.\nStopped";
1781 print "Defaulting $key to $opts{$key}\n" if $opts{'verbose'};
1783 elsif ($key eq 'java') {
1785 $opts{'java'} = default_java_home();
1787 if ($opts{'java'} eq '') {
1788 die "ERROR: --$key requires a value.\nStopped";
1791 elsif (exists $optdep{$key}->{env} && !$optdep{$key}->{may_be_blank}) {
1792 die "ERROR: --$key requires a value.\nStopped";
1797 sub has_feature {
1798 my $feat = shift;
1799 for my $f (@features) {
1800 my ($key, $value) = split(/=/, $f);
1801 if ($key eq $feat) {
1802 return 1;
1805 return 0;
1808 if ($opts{'java'}) {
1809 push(@features, 'java=1');
1811 my $feat = 'java_pre_jpms';
1812 unless (has_feature($feat)) {
1813 my $javac = File::Spec->catfile($opts{'java'}, 'bin', 'javac');
1814 open(my $vers, "\"$javac\" -version |");
1815 while (<$vers>) {
1816 if (/javac (\d+)/) {
1817 print "Found javac major version $1\n" if $opts{'verbose'};
1818 if ($1 > 1) {
1819 push(@features, "$feat=0");
1820 print "Feature $feat=0 due to Java version\n" if $opts{'verbose'};
1822 last;
1828 sub env_from_opt {
1829 my $key = shift;
1830 my $e = shift;
1831 my $notdir = shift;
1832 if ($opts{$key}) {
1833 setEnv($e, $opts{$key}, $notdir);
1837 # Enable MPC features and populate expected environment variables
1838 # for all required "optional" dependencies.
1839 for my $key (keys %optdep) {
1840 if (exists $opts{$key}) {
1841 print "Enabling $key\n" if $opts{'verbose'};
1842 my $e = $optdep{$key}->{env};
1843 my $s = $optdep{$key}->{sanity};
1844 my $m = $optdep{$key}->{mpc};
1845 if ($opts{$key} ne 'skip_version_check') {
1846 env_from_opt($key, $e) if defined $e;
1847 if (ref $s eq 'HASH') {
1848 my $ok = 0;
1849 for my $alt (keys %{$s}) {
1850 if (-r $targetEnv{$e} . '/' . $alt) {
1851 print "Found $key at $targetEnv{$e} using alternative $alt\n"
1852 if $opts{'verbose'};
1853 $ok = 1;
1854 push(@features, $s->{$alt}) if defined $s->{$alt};
1855 last;
1857 print "Didn't find $key at $targetEnv{$e} using alternative $alt\n"
1858 if $opts{'verbose'};
1860 unless ($ok) {
1861 die "ERROR: Can't find $key at $targetEnv{$e}.\nStopped";
1864 elsif ($s && !-r $targetEnv{$e} . '/' . $s) {
1865 die "ERROR: Can't find $key at $targetEnv{$e} (using $s).\nStopped";
1868 push(@features, $m) if $m;
1869 push(@platformmacros, $m) if $need_platform_macros{$key};
1873 if ($is_windows && !$opts{'optimize'}) { # look for nonstandard vcpkg layout
1874 for my $key ('xerces3', 'openssl') {
1875 my $env = $optdep{$key}->{env};
1876 if ($opts{$key} && $targetEnv{$env}) {
1877 if (-d $targetEnv{$env} . '/debug/lib') {
1878 print "Using debug/lib subdir for $key\n" if $opts{'verbose'};
1879 my $libenv = $env;
1880 $libenv =~ s/_?ROOT$/_LIBDIR/;
1881 setEnv($libenv, $targetEnv{$env} . '/debug/lib');
1882 $opts{$key . '-debugbin'} = 1;
1888 ## OpenSSL version-detection / feature-injection
1889 if ($opts{'openssl'} && $opts{'openssl'} ne 'skip_version_check') {
1890 my $ssl_version_file = $opts{'openssl'} . '/' . $optdep{'openssl'}->{sanity};
1892 open my $fh, '<', $ssl_version_file
1893 or die "ERROR: Failed to open '$ssl_version_file' for OpenSSL version detection.\nStopped";
1895 while (<$fh>) {
1896 if (/^\s*#\s*define\s*OPENSSL_VERSION_TEXT\s*"([^"]*)"/) {
1897 my $ver = $1;
1898 print "OpenSSL version detected: \"$ver\"\n" if $opts{'verbose'};
1899 if ($ver =~ /OpenSSL[\W]+1\.1/ || $ver =~ /OpenSSL[\W]+3\./) {
1900 print "OpenSSL 1.1+ MPC feature enabled\n" if $opts{'verbose'};
1901 push(@features, 'openssl11=1');
1903 last;
1908 sub win32_cmake_generator {
1909 my $ver = shift;
1911 my %map = ('vc9' => 'Visual Studio 9 2008',
1912 'vc10' => 'Visual Studio 10 2010',
1913 'vc11' => 'Visual Studio 11 2012',
1914 'vc12' => 'Visual Studio 12 2013',
1915 'vc14' => 'Visual Studio 14 2015',
1916 'vs2017' => 'Visual Studio 15 2017',
1917 'vs2019' => 'Visual Studio 16 2019',
1918 'vs2022' => 'Visual Studio 17 2022');
1920 die "ERROR: Unsupported Visual Studio version '$ver' supplied.\nStopped"
1921 if ! exists $map{$ver};
1923 return $map{$ver};
1926 if ($build_gtest) {
1927 print("Building GoogleTest...\n");
1929 my $cwd = Cwd::getcwd();
1930 my $build_dir = $opts{'gtest'} . $slash . 'build';
1931 my $install_dir = $build_dir . $slash . 'install';
1932 my $cmake = $opts{'cmake'};
1933 my @cmake_platform_args = $is_windows ?
1934 ('-Dgtest_force_shared_crt=ON',
1935 '-DCMAKE_CXX_FLAGS=/D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1',
1936 '-G', '"' . win32_cmake_generator($opts{'compiler_version'}) . '"',
1937 '-A', $opts{'compiler_target_architecture'}) : ();
1938 if ($opts{'nonstdcompiler'}) {
1939 push @cmake_platform_args, "-DCMAKE_CXX_COMPILER=$opts{'compiler'}";
1941 my @cmake_cmds = ([$cmake,
1942 "-DCMAKE_INSTALL_PREFIX=$install_dir",
1943 '-DCMAKE_INSTALL_LIBDIR=lib',
1944 '-DCMAKE_POSITION_INDEPENDENT_CODE=TRUE',
1945 @cmake_platform_args,
1946 '..'],
1947 ["$cmake", "--build", ".", "--target", "install"]);
1949 mkdir($build_dir)
1950 or die "ERROR '$!': failed to make directory $build_dir from $cwd.\nStopped"
1951 if ! -d $build_dir;
1953 mkdir($install_dir)
1954 or die "ERROR '$!': failed to make directory $install_dir from $cwd.\nStopped"
1955 if ! -d $install_dir;
1957 my $cd_build_dir = ChangeDir->new($build_dir);
1958 for my $cmd (@cmake_cmds) {
1959 run_command($cmd, capture => [undef, dump_on_failure => 1]) == 0
1960 or die "ERROR: Invoking @{$cmd} failed.\nStopped";
1963 print("Done Building GoogleTest\n");
1966 if (exists $opts{'jboss'} && !exists $opts{'java'}) {
1967 die "ERROR: --java is required for --jboss (OpenDDS JMS Provider).\nStopped";
1970 if (exists $opts{'jboss'} && !exists $opts{'ant'}) {
1971 die "ERROR: --ant is required for --jboss (OpenDDS JMS Provider).\nStopped";
1974 if (exists $opts{'wireshark-cmake'}) {
1975 if (exists $opts{'wireshark'}) {
1976 die "ERROR: --wireshark and --wireshark-cmake can not be used at the same time.\nStopped";
1979 if (! -d ($opts{'wireshark-build'} . '/' . $opts{'wireshark-lib'})) {
1980 if ($wireshark_lib_defaulted) {
1981 die "ERROR: The default value for wireshark-lib: " . $opts{'wireshark-lib'} .
1982 " does not exist, please supply wireshark-lib with the correct value";
1984 else {
1985 die "ERROR: The supplied wireshark-lib: " . $opts{'wireshark-lib'} . " does not exist.";
1990 if (exists $opts{'wireshark-cmake'} && !exists $opts{'wireshark-build'}) {
1991 die "ERROR: --wireshark-build are required with --wireshark-cmake.\nStopped";
1994 if (exists $opts{'wireshark-lib'} && !exists $opts{'wireshark-cmake'}) {
1995 die "ERROR: --wireshark-cmake and --wireshark-build is required for --wireshark-lib.\nStopped";
1998 if (exists $opts{'wireshark-build'} && !exists $opts{'wireshark-cmake'}) {
1999 die "ERROR: --wireshark-cmake is required for --wireshark-build.\nStopped";
2002 if ($opts{'glib'}) {
2003 if (!-r "$opts{'glib'}/lib/glib-2.0/include/glibconfig.h") {
2004 my $pc = `pkg-config --cflags-only-I glib-2.0`;
2005 chomp $pc;
2006 if ($pc =~ m!-I$opts{'glib'}/([-\w/]+)/glib-2.0/include!) {
2007 print "pkg-config found GLIB_LIB_DIR=$1\n" if $opts{'verbose'};
2008 setEnv('GLIB_LIB_DIR', $1);
2010 else {
2011 print "Failed to locate glibconfig.h using pkg-config ($pc). Set the " .
2012 "environment variable GLIB_LIB_DIR and re-run MPC.\n";
2017 sub push_env_dir {
2018 my ($buildEnvRef, $var, $path) = @_;
2019 if (exists $buildEnvRef->{$var}) {
2020 $buildEnvRef->{$var} .= $specific{'pathsep'} . $path;
2022 else {
2023 $buildEnvRef->{$var} = $path;
2027 sub push_path {
2028 my ($buildEnvRef, $path) = @_;
2029 push_env_dir($buildEnvRef, 'PATH', $path);
2032 sub push_libpath {
2033 my ($buildEnvRef, $path) = @_;
2034 my $build = $buildEnvRef->{'build'};
2035 my $platform = $opts{$build};
2036 my $libpathname = $platforminfo{$platform}->{'libpath'};
2037 push_env_dir($buildEnvRef, $libpathname, $path);
2040 # Qt5
2041 if (exists $opts{'qt'}) {
2042 my $qglobal = "QtCore" . $slash . "qglobal.h";
2043 my $qt_include;
2044 my $qt_bin; # location of Qt's code generators (like moc)
2045 my $qt_lib; # where linker should look for input (location of .lib or .so)
2046 my $qt_path; # add to PATH or LD_LIBRARY_PATH (location of .dll or .so.*)
2047 my $qt_bin_suffix = exists $ENV{'QT5_SUFFIX'} ? $ENV{'QT5_SUFFIX'} : '';
2048 my $qt_moc = "moc" . $qt_bin_suffix . $exeext;
2049 my $qt_default_suffix = "-qt5";
2050 my $qt_moc_default_suffix = "moc" . $qt_default_suffix . $exeext;
2051 my $qt_help_mesg = ", stopped\nPlease either install Qt or provide the correct " .
2052 "Qt5 locations (see docs/qt.md for details).\n";
2054 if ($try_to_use_qt_system_pkg) {
2055 # Try to use pkg-config to get Qt locations
2056 `pkg-config --print-variables Qt5Core`;
2057 if ($?) {
2058 die "ERROR: Trying to use system Qt package but could not confirm " .
2059 "Qt and/or pkg-config exists on the system$qt_help_mesg";
2061 $qt_include = `pkg-config --variable=includedir Qt5Core`;
2062 die "ERROR: could not resolve system Qt include location$qt_help_mesg" if ($?);
2063 chomp $qt_include;
2064 $qt_bin = `pkg-config --variable=host_bins Qt5Core`;
2065 die "ERROR: could not resolve system Qt build tools location$qt_help_mesg" if ($?);
2066 chomp $qt_bin;
2067 $qt_lib = `pkg-config --variable=libdir Qt5Core`;
2068 die "ERROR: could not resolve system Qt lib location$qt_help_mesg" if ($?);
2069 chomp $qt_lib;
2071 else {
2072 if (exists $opts{'qt-include'}) {
2073 if (!length($opts{'qt-include'})) {
2074 die "ERROR: --qt-include requires an argument$qt_help_mesg";
2076 $qt_include = $opts{'qt-include'};
2078 else {
2079 $qt_include = $targetEnv{'QTDIR'} . $slash . 'include';
2082 if (exists $ENV{'QT5_BINDIR'}) {
2083 $qt_bin = $ENV{'QT5_BINDIR'};
2085 else {
2086 $qt_bin = $targetEnv{'QTDIR'} . $slash . 'bin';
2089 if (exists $ENV{'QT5_LIBDIR'}) {
2090 $qt_lib = $ENV{'QT5_LIBDIR'};
2092 else {
2093 $qt_lib = $targetEnv{'QTDIR'} . $slash . ($is_windows ? 'bin' : 'lib');
2096 $qt_path = $qt_lib;
2098 # Try to guess if Qt vcpkg is being used
2099 my $vcpkg_tools = join($slash, $targetEnv{'QTDIR'}, "tools", "qt5");
2100 my $assume_vcpkg =
2101 (-r $vcpkg_tools . $slash . 'bin' . $slash . $qt_moc) &&
2102 !exists $ENV{'QT5_LIBDIR'} &&
2103 !exists $ENV{'QT5_BINDIR'};
2104 if ($assume_vcpkg) {
2105 $qt_bin = $vcpkg_tools . $slash . 'bin';
2106 if ($debug) {
2107 $qt_lib = join($slash, $targetEnv{'QTDIR'}, "debug", "lib");
2108 $qt_path = join($slash, $targetEnv{'QTDIR'}, "debug",
2109 ($is_windows ? 'bin' : 'lib'));
2111 else {
2112 $qt_lib = join($slash, $targetEnv{'QTDIR'}, 'lib');
2116 # Check for Qt Headers using qglobal.h
2117 # Also try to detect if Qt Headers were just put in the include directory or
2118 # put in qt5 directory inside the include directory. For example, the former
2119 # might happen on Windows with prebuilt Qt while the latter might be the
2120 # case with Linux with Qt installed from package manager.
2121 if (! -r $qt_include . $slash . $qglobal) {
2122 my $qt_subdir = $qt_include . $slash . 'qt5';
2123 if (-r $qt_subdir . $slash . $qglobal) {
2124 $qt_include = $qt_subdir;
2126 else {
2127 die "ERROR: Could not find Qt headers at $qt_include" .
2128 " ($qglobal could not be found)$qt_help_mesg";
2132 # Check for Qt Tools using moc
2133 if (! -r $qt_bin . $slash . $qt_moc) {
2134 # If QT5_SUFFIX isn't defined, maybe it needs a suffix
2135 if ((!exists $ENV{'QT5_SUFFIX'}) &&
2136 (-r $qt_bin . $slash . $qt_moc_default_suffix)) {
2137 $qt_bin_suffix = $qt_default_suffix;
2139 else {
2140 die "ERROR: Could not find Qt build tools at $qt_bin" .
2141 " ($qt_moc could not be found)$qt_help_mesg";
2145 setEnv('QT5_INCDIR', $qt_include);
2146 setEnv('QT5_BINDIR', $qt_bin);
2147 setEnv('QT5_SUFFIX', $qt_bin_suffix);
2148 setEnv('QT5_LIBDIR', $qt_lib);
2149 push_libpath(\%targetEnv, $qt_path);
2151 elsif (exists $opts{'qt-include'}) {
2152 die "ERROR: --qt-include requires --qt, stopped\n";
2155 sub write_host_workspace {
2156 my %buildEnv = %{shift()};
2157 my $MWC = backup_and_open($buildEnv{'DDS_ROOT'} . '/host_tools.mwc');
2158 print $MWC <<'EOT';
2159 workspace {
2160 $(ACE_ROOT)/ace/ace.mpc
2161 $(ACE_ROOT)/apps/gperf/src
2162 $(TAO_ROOT)/TAO_IDL
2163 dds/DCPS/OpenDDS_Util.mpc
2164 dds/idl
2165 java/idl2jni/codegen
2167 print $MWC " \$(TAO_ROOT)/tao/tao.mpc\n" if $opts{'safety-profile'};
2168 print $MWC "}\n";
2169 $MWC->close;
2170 print "Wrote host_tools.mwc in $buildEnv{'DDS_ROOT'}\n" if $opts{'verbose'};
2171 dump_and_unlink($MWC) if $opts{'dry-run'};
2174 sub mergeToEnv {
2175 my $buildEnv = shift;
2176 for my $k (keys %{$buildEnv}) {
2177 next if $k eq 'build';
2178 if ($buildEnv->{$k} =~
2179 /^\Q$specific{'refpre'}\E$k\Q$specific{'refpost'}\E(.*)/) {
2180 if ($1 ne '') {
2181 $ENV{$k} .= $1;
2182 print "ENV: Appending $1 to $k\n" if $opts{'verbose'};
2185 else {
2186 $ENV{$k} = $buildEnv->{$k};
2187 print "ENV: Setting $k to $buildEnv->{$k}\n" if $opts{'verbose'};
2192 sub disable_feature {
2193 my($featureArray, $feature) = @_;
2194 $feature =~ s/-/_/g;
2195 push(@{$featureArray}, "$feature=0");
2198 sub get_default_features {
2199 my $buildEnv = shift;
2201 my %default_features;
2202 my $file = "$buildEnv->{ACE_ROOT}/bin/MakeProjectCreator/config/default.features";
2203 if (-e $file) {
2204 open(my $fh, '<', $file) or die "ERROR: Could not open $file: $!";
2205 while (my $row = <$fh>) {
2206 chomp $row;
2207 $row =~ s/\/\/.*//;
2208 $row =~ s/^\s+//;
2209 $row =~ s/\s+$//;
2210 next if $row eq '';
2211 my ($key, $value) = split(/\s*=\s*/, $row);
2212 $default_features{$key} = $value;
2215 return %default_features;
2218 sub get_requested_features {
2219 my %requested_features;
2220 for my $f (@features) {
2221 my ($key, $value) = split(/=/, $f);
2222 $value = "1" unless defined $value;
2223 $requested_features{$key} = $value;
2225 return %requested_features;
2228 sub get_features {
2229 my $buildEnv = shift;
2231 my %features = get_default_features($buildEnv);
2232 my %requested_features = get_requested_features();
2233 for my $key (keys(%requested_features)) {
2234 $features{$key} = $requested_features{$key};
2236 return %features;
2239 sub write_opendds_mwc {
2240 my $root = shift;
2241 my $args = shift;
2243 $args = join(', ', (map {perlstring($_)} @{$args}));
2245 my $path = "$root/bin/opendds_mwc.pl";
2246 my $fd = backup_and_open($path);
2247 print $fd <<"EOF";
2248 #!/usr/bin/env perl
2249 if (!defined(\$ENV{ACE_ROOT}) || !defined(\$ENV{MPC_ROOT})) {
2250 die("The enviroment needs to be setup.");
2252 system('perl', "\$ENV{ACE_ROOT}/bin/mwc.pl", $args, \@ARGV) == 0
2253 or die("Failed to run mwc: \$!");
2255 $fd->close();
2256 print "Wrote $path\n" if $opts{'verbose'};
2257 dump_and_unlink($fd) if $opts{'dry-run'};
2259 chmod(0775, $path);
2262 my $buildtao;
2263 sub generate_workspace {
2264 my $buildEnv = shift;
2265 $buildtao = $force_ace_tao;
2266 my $is_target = $buildEnv->{'build'} eq 'target';
2268 for my $feat (qw/built-in-topics ownership-profile/) {
2269 if (exists $opts{$feat} && !$opts{$feat}) {
2270 disable_feature(\@features, $feat);
2274 if ($opts{'security'}) {
2275 disable_feature(\@features, 'no-opendds-security');
2278 # default of these depends on whether we are doing a safety-profile build
2279 for my $feat (qw/content-subscription content-filtered-topic
2280 multi-topic query-condition ownership-kind-exclusive
2281 object-model-profile persistence-profile
2282 no-opendds-safety-profile/) {
2283 if (defined $opts{'no-opendds-safety-profile'}) {
2284 if (!(exists $opts{$feat} && $opts{$feat})) {
2285 disable_feature(\@features, $feat);
2288 elsif (exists $opts{$feat} && !$opts{$feat}) {
2289 disable_feature(\@features, $feat);
2293 if ($is_target && $opts{'target'} eq 'android') {
2294 # We can't detect C++11 compatibility in cross compilers at the moment,
2295 # but all the NDKs we officially support are C++11+ by default, so enable
2296 # C++11 features unless an explicit 'no_cxx11' feature already is set.
2297 my $feat = 'no_cxx11';
2298 unless (has_feature($feat)) {
2299 disable_feature(\@features, $feat);
2300 print "Setting $feat=0 for Android\n";
2303 # Disable including $JAVA_HOME/include on Android. Android NDK includes a
2304 # jni.h
2305 disable_feature(\@features, 'jni_include')
2308 my $mpctype = (!$is_windows || ($cross_compile && $is_target))
2309 ? 'gnuace' : $opts{'compiler_version'};
2311 if ($mpctype eq 'gnuace') {
2312 my $UM;
2313 if (@features || $cross_compile) {
2314 print "Writing $buildEnv->{'DDS_ROOT'}/user_macros.GNU\n"
2315 if $opts{'verbose'};
2316 $UM = backup_and_open($buildEnv->{'DDS_ROOT'} . '/user_macros.GNU');
2318 if ($is_target) {
2319 for my $feat (@features) {
2320 my $key = $feat;
2321 $key =~ s/=.*//;
2322 print $UM ($feat =~ /=/ ? $feat : "$feat=1"), "\n"
2323 unless $need_platform_macros{$key};
2325 if ($cross_compile) {
2326 print $UM <<'EOT';
2327 OPENDDS_IDL = $(HOST_DDS)/bin/opendds_idl
2328 OPENDDS_IDL_DEP = $(OPENDDS_IDL)$(HOST_EXE_EXT)
2329 IDL2JNI = $(HOST_DDS)/bin/idl2jni
2330 IDL2JNI_DEP = $(IDL2JNI)$(HOST_EXE_EXT)
2334 $UM->close if $UM;
2335 dump_and_unlink($UM) if $UM && $opts{'dry-run'};
2338 if ($opts{'safety-profile'} && $is_target) {
2339 $buildtao = 0; # TAO will be built separately
2341 else {
2342 # build tao if tao_idl does not exist
2343 $buildtao = $buildtao || !-x "$buildEnv->{'ACE_ROOT'}/bin/tao_idl$exeext";
2346 locate_mpc($ace_src);
2348 # Append to default.features
2349 if ($is_target) {
2350 push(@features, 'cross_compile') if $cross_compile;
2352 if ($platforminfo{$opts{'target'}}->{'needs_i2jrt_corba'} && $opts{'java'}) {
2353 if ($opts{'verbose'}) {
2354 print "Target platform needs i2jrt_corba.jar, forcing java_pre_jpms=0 to get it\n";
2356 my $found = 0;
2357 for my $i (0 .. scalar $#features) {
2358 if ($features[$i] =~ /^java_pre_jpms/) {
2359 $features[$i] = 'java_pre_jpms=0';
2360 my $found = 1;
2363 if (!$found) {
2364 push(@features, "java_pre_jpms=0");
2368 if ($opts{'java'} && $cross_compile) {
2369 push(@features, "jni_check=0");
2372 if ($buildtao && @features) {
2373 my $df_file = $buildEnv->{'ACE_ROOT'} .
2374 '/bin/MakeProjectCreator/config/default.features';
2375 my $DF = $opts{'dry-run'} ? File::Temp->new()
2376 : new FileHandle(">>$df_file");
2377 for my $f (@features) {
2378 print $DF ($f =~ /=/ ? $f : "$f=1"), "\n";
2380 $DF->close;
2381 if ($opts{'verbose'}) {
2382 print '' . ($wrote_df ? 'Appended to' : 'Wrote') .
2383 " $buildEnv->{'ACE_ROOT'}/.../default.features\n";
2385 dump_and_unlink($DF) if $opts{'dry-run'};
2388 if (!$buildtao && !defined $opts{'safety-profile'}) {
2389 # Check for compatibility between the requested features/macros
2390 # and the pre-built ACE/TAO.
2391 my %existing_features = get_default_features($buildEnv);
2392 my %requested_features = get_requested_features();
2393 for my $f (@ace_features) {
2394 if (defined $requested_features{$f}) {
2395 if (!defined $existing_features{$f}) {
2396 die "ERROR: Requested feature $f not defined for ACE/TAO";
2398 elsif ($requested_features{$f} != $existing_features{$f}) {
2399 die "ERROR: Feature $f set to $requested_features{$f} but has " .
2400 "value $existing_features{$f} in ACE/TAO";
2405 my $file = "$buildEnv->{'ACE_ROOT'}/include/makeinclude/platform_macros.GNU";
2406 my %existing_macros;
2407 if (-e $file) {
2408 open(my $fh, '<', $file) or die "ERROR: Could not open $file: $!";
2409 while (my $row = <$fh>) {
2410 chomp $row;
2411 $row =~ s/\s*#.*$//;
2412 if ($row =~ /^\s*(\w+)\s*[:?+]?=\s*(.*)$/) {
2413 my ($key, $value) = ($1, $2);
2414 $existing_macros{$key} = $value;
2417 my %requested_macros;
2418 for my $f (@platformmacros, @{$opts{'macros'}}) {
2419 my ($key, $value) = split(/=/, $f);
2420 $value = "1" unless defined $value;
2421 $requested_macros{$key} = $value;
2423 for my $key (@ace_macros) {
2424 if (exists $opts{$key}) {
2425 my $macro = ($key eq 'static') ? 'static_libs_only' : $key;
2426 $requested_macros{$macro} = $opts{$key};
2429 for my $f (@ace_macros) {
2430 if (defined $requested_macros{$f}) {
2431 if (!defined $existing_macros{$f}) {
2432 die "ERROR: Requested macro $f not defined for ACE/TAO";
2434 elsif ($requested_macros{$f} != $existing_macros{$f}) {
2435 die "ERROR: Macro $f set to $requested_macros{$f} but has " .
2436 "value $existing_macros{$f} in ACE/TAO";
2444 my $custom_ws = "OpenDDS_custom.mwc";
2445 if (exists($opts{'workspace'})) {
2446 backup_and_copy($opts{'workspace'}, $custom_ws);
2449 my $ws;
2450 if (-r "$buildEnv->{'DDS_ROOT'}/host_tools.mwc") {
2451 $ws = 'host_tools.mwc';
2453 elsif (-r $custom_ws) {
2454 $ws = $custom_ws;
2456 elsif (!$tests && $buildtao) {
2457 $ws = 'DDS_TAOv2.mwc';
2459 elsif (!$tests && !$buildtao) {
2460 $ws = 'DDS_no_tests.mwc';
2462 elsif ($buildtao) {
2463 $ws = 'DDS_TAOv2_all.mwc';
2465 else {
2466 $ws = 'DDS.mwc';
2469 my $static = (($opts{'static'} && $is_windows) ||
2470 ($cross_compile && $buildEnv->{'build'} eq 'host' && $mpctype ne 'gnuace'));
2472 # We are not using CIAO or DAnCE, but MPC.cfg expands $CIAO_ROOT and
2473 # $DANCE_ROOT so leaving them empty/undefined would cause /MPC/config
2474 # to be on the include path for .mpb files.
2475 for my $var ('CIAO_ROOT', 'DANCE_ROOT') {
2476 $buildEnv->{$var} = 'unused' unless defined $buildEnv->{$var};
2479 my %savedEnv = %ENV;
2480 print "ENV: saving current environment\n" if $opts{'verbose'};
2481 mergeToEnv($buildEnv);
2483 my @mwc_common_args = ('-type', $mpctype, @mpcopts);
2484 if ($static) {
2485 push(@mwc_common_args, '-static');
2488 if (!$buildtao) {
2489 my %existing_features = get_default_features($buildEnv);
2490 my %requested_features = get_requested_features();
2491 my @features_to_add;
2492 for my $feature (keys(%requested_features)) {
2493 my $value = $requested_features{$feature};
2494 push(@features_to_add, "$feature=$value") unless exists($existing_features{$feature});
2496 if (@features_to_add) {
2497 push(@mwc_common_args, '-features', join(',', @features_to_add));
2501 # Generate our own mwc wrapper script
2502 write_opendds_mwc($buildEnv->{'DDS_ROOT'}, [@mwc_common_args]);
2504 my @mwc = ('perl', "$ENV{ACE_ROOT}/bin/mwc.pl");
2505 print 'Running MPC to generate ', ($mpctype eq 'gnuace' ? 'makefiles' :
2506 'project files'), ".\n";
2507 if (run_command([@mwc, @mwc_common_args, "$buildEnv->{'DDS_ROOT'}$slash$ws"])) {
2508 die "ERROR: Error from MPC, stopped";
2510 $buildEnv->{'mpctype'} = $mpctype;
2512 # If this is a target safety profile build
2513 if (defined $opts{'no-opendds-safety-profile'}) {
2514 # Generate ACE workspace separately, to exclude TAO
2515 if (run_command([@mwc, @mwc_common_args, "$buildEnv->{'ACE_ROOT'}/ace"])) {
2516 die "ERROR: Error from MPC, stopped";
2520 %ENV = %savedEnv;
2521 print "ENV: restoring previous environment\n" if $opts{'verbose'};
2523 $ws =~ s/\.mwc$/.sln/;
2524 $opts{'solution_file'} = $ws;
2527 sub convert_environment {
2528 my $val = shift;
2529 $$val =~ s/\\/\//g;
2530 $$val =~ s/%(\w+)%/\$\{$1\}/g;
2533 sub write_environment {
2534 my %buildEnv = %{shift()};
2535 my $dir = shift;
2536 my $MK = undef;
2537 if ($buildEnv{'mpctype'} eq 'gnuace') {
2538 if (!$opts{'dry-run'}) {
2539 move($dir . '/GNUmakefile', $dir . '/GNUmakefile.dist');
2541 $MK = backup_and_open($dir . '/GNUmakefile');
2544 my $SE = backup_and_open($dir . '/setenv.' . $specific{'ext'});
2546 my $args_ = join(' ', map {/ / ? ('"' . $_ . '"') : $_} @ARGS);
2547 print $SE "$specific{'comment'} OpenDDS configure script: $0 $args_\n";
2548 print $MK "$specific{'comment'} OpenDDS configure script: $0 $args_\n" if $MK;
2550 for my $key (sort(keys(%buildEnv))) {
2551 if ($key ne 'build' && $key ne 'mpctype') {
2552 my $value = $buildEnv{$key};
2553 $value =~ s/^"(.*)"$/$1/g;
2555 if ($is_windows) {
2556 print $SE "set \"$key=$value\"\n";
2558 else {
2559 my $posix_value = $value;
2560 $posix_value =~ s/"/\\"/g;
2561 print $SE "export $key=\"$posix_value\"\n";
2564 if ($MK) {
2565 convert_environment(\$value) if $is_windows;
2566 print $MK "export $key := $value\n";
2571 $SE->close;
2572 print "Wrote $dir/setenv.$specific{'ext'}\n" if $opts{'verbose'};
2573 dump_and_unlink($SE) if $opts{'dry-run'};
2575 if ($MK) {
2576 print $MK "include GNUmakefile.dist\n";
2577 $MK->close;
2578 print "Wrote $dir/GNUmakefile, wrapping original GNUmakefile.dist\n"
2579 if $opts{'verbose'};
2580 dump_and_unlink($MK) if $opts{'dry-run'};
2584 sub check_mac_version {
2585 my $buildEnvRef = shift;
2586 if (-r "$buildEnvRef->{ACE_ROOT}/ace/config-macosx.h") {
2587 my $cfg = new FileHandle("$buildEnvRef->{ACE_ROOT}/ace/config-macosx.h");
2588 while (<$cfg>) {
2589 if (/__MAC_OS_X_VERSION_MAX_ALLOWED/) {
2590 return;
2595 die "ERROR: This version of ACE doesn't contain the required configuration " .
2596 "files for Apple macOS. Stopped";
2599 sub configure_build {
2600 my $buildEnvRef = shift;
2601 if (-r "$buildEnvRef->{ACE_ROOT}/ace/config.h" && !$force_ace_tao) {
2602 print "ACE_ROOT/ace/config.h exists, skipping configuration of ACE+TAO\n";
2604 else {
2605 check_mac_version($buildEnvRef) if $opts{'host'} eq 'macosx';
2607 write_config_h($buildEnvRef);
2608 write_default_features($buildEnvRef);
2609 write_platform_macros($buildEnvRef);
2611 write_opendds_configh($buildEnvRef);
2612 generate_workspace($buildEnvRef);
2613 write_environment($buildEnvRef, $buildEnvRef->{'DDS_ROOT'});
2615 if ($buildEnvRef->{'build'} eq 'target') {
2616 write_cmake_file($buildEnvRef);
2620 sub write_cross_compile_makefile {
2621 my $MF = backup_and_open('GNUmakefile');
2622 my $safety_profile_step = $opts{'safety-profile'} ?
2623 "\tcd $targetEnv{'ACE_ROOT'}/ace && " .
2624 '$(MAKE) $(if $(filter all,$@),ACE,$@)' . "\n" : '';
2625 print $MF <<"EOT";
2626 # OpenDDS configure script: $0 @ARGS
2627 all clean realclean depend:
2628 \tcd build/host && \$(MAKE) \$@
2629 $safety_profile_step\tcd build/target && \$(MAKE) \$@
2630 .PHONY: all clean realclean depend
2632 $MF->close;
2633 print "Wrote top-level GNUmakefile for cross-compile\n" if $opts{'verbose'};
2634 dump_and_unlink($MF) if $opts{'dry-run'};
2637 sub nested {
2638 my($sub, $top) = @_;
2639 my $subdir = $opts{'dry-run'} ? $sub : Cwd::abs_path($sub);
2640 my $parent = $opts{'dry-run'} ? $top : Cwd::abs_path($top);
2641 if ($opts{'dry-run'} && $top eq '.' && looksRelative($sub)) {
2642 return $slash . $sub;
2644 if (index($subdir, $parent) == 0) {
2645 return substr($subdir, length($parent));
2647 return undef;
2650 sub add_dependency_paths {
2651 my %dirs;
2652 for my $key ('openssl', 'xerces3') {
2653 if ($opts{$key}) {
2654 next if $opts{$key} eq '/usr' || $opts{$key} eq 'skip_version_check';
2655 my $suffix = $slash . 'bin';
2656 if ($opts{$key . '-debugbin'}) {
2657 $suffix = $slash . 'debug' . $suffix;
2659 $dirs{$targetEnv{$optdep{$key}->{env}} . $suffix} = 1;
2662 for my $d (keys %dirs) {
2663 push_libpath(\%targetEnv, $d);
2666 if ($opts{'openssl'} && $ENV{'SSL_LIBDIR'}) {
2667 push_libpath(\%targetEnv, $ENV{'SSL_LIBDIR'});
2671 my $cloned_build = $cross_compile && $build_host_tools;
2672 if ($cloned_build) {
2673 clone_host_and_target('.');
2674 setHostEnv('DDS_ROOT', 'build/host');
2675 setEnv('HOST_DDS', 'build/host');
2676 setEnv('DDS_ROOT', 'build/target');
2678 my $tao_clone = $opts{'safety-profile'} ? 'host' : 'target';
2679 my $ace_sub_dds = nested($ace_src, '.');
2680 my $tao_sub_dds = nested($tao_src, '.');
2681 my $tao_sub_ace = nested($tao_src, $ace_src);
2683 if ($ace_sub_dds) {
2684 setHostEnv('ACE_ROOT', 'build/host' . $ace_sub_dds);
2685 setEnv('HOST_ACE', 'build/host' . $ace_sub_dds);
2686 setEnv('ACE_ROOT', 'build/target' . $ace_sub_dds);
2688 else {
2689 clone_host_and_target($ace_src);
2690 setHostEnv('ACE_ROOT', $ace_src . '/build/host');
2691 setEnv('HOST_ACE', $ace_src . '/build/host');
2692 setEnv('ACE_ROOT', $ace_src . '/build/target');
2693 if ($tao_sub_ace) {
2694 setHostEnv('TAO_ROOT', $ace_src . '/build/host' . $tao_sub_ace);
2695 setEnv('TAO_ROOT', $ace_src . '/build/' . $tao_clone . $tao_sub_ace);
2699 if ($tao_sub_dds) {
2700 setHostEnv('TAO_ROOT', 'build/host' . $tao_sub_dds);
2701 setEnv('TAO_ROOT', 'build/' . $tao_clone . $tao_sub_dds);
2703 elsif (!$tao_sub_ace) {
2704 if ($opts{'safety-profile'}) {
2705 setHostEnv('TAO_ROOT', $tao_src);
2706 setEnv('TAO_ROOT', $tao_src);
2708 else {
2709 clone_host_and_target($tao_src);
2710 setHostEnv('TAO_ROOT', $tao_src . '/build/host');
2711 setEnv('TAO_ROOT', $tao_src . '/build/target');
2715 push_path(\%targetEnv, $hostEnv{'ACE_ROOT'} . $slash . 'bin');
2716 push_path(\%targetEnv, $hostEnv{'DDS_ROOT'} . $slash . 'bin');
2718 print "Cross-compile configuring host\n";
2720 write_host_workspace(\%hostEnv); # host_tools.mwc
2721 configure_build(\%hostEnv);
2723 print "Cross-compile configuring target\n";
2725 if ($opts{'safety-profile'}) {
2726 $opts{'no-opendds-safety-profile'} = 0; # only set for target
2729 unless ($opts{'static'}) {
2730 push_libpath(\%targetEnv, $targetEnv{'ACE_ROOT'} . $slash . 'lib');
2731 push_libpath(\%targetEnv, $targetEnv{'DDS_ROOT'} . $slash . 'lib');
2733 add_dependency_paths();
2736 configure_build(\%targetEnv);
2738 write_environment(\%targetEnv, $targetEnv{'ACE_ROOT'} . '/ace')
2739 if $opts{'safety-profile'};
2741 write_cross_compile_makefile() if !$is_windows;
2743 else { # does not require cloned builds
2744 setEnv('ACE_ROOT', $ace_src);
2745 setEnv('TAO_ROOT', $tao_src);
2746 setEnv('DDS_ROOT', '.');
2748 if (!$build_host_tools && $opts{'host-tools'}) {
2749 setEnv('HOST_DDS', $opts{'host-tools'});
2752 push_path(\%targetEnv, $targetEnv{'ACE_ROOT'} . $slash . 'bin');
2753 push_path(\%targetEnv, $targetEnv{'DDS_ROOT'} . $slash . 'bin');
2755 unless ($opts{'static'}) {
2756 push_libpath(\%targetEnv, $targetEnv{'ACE_ROOT'} . $slash . 'lib');
2757 push_libpath(\%targetEnv, $targetEnv{'DDS_ROOT'} . $slash . 'lib');
2759 add_dependency_paths();
2762 if ($host_tools_only) {
2763 write_host_workspace(\%targetEnv);
2766 configure_build(\%targetEnv);
2769 print "Completed configuring OpenDDS, next ";
2770 if (!$is_windows || ($cross_compile && !$cloned_build)) {
2771 print "run '", (($^O eq 'freebsd') ? 'g' : ''), "make'";
2773 elsif ($cloned_build) {
2774 print "compile build\\host\\host_tools.sln\n",
2775 "using the environment in build\\host\\setenv.cmd.\n",
2776 "Then build OpenDDS for $opts{'target'} by running make in ",
2777 "build\\target.\n";
2778 exit 0;
2780 else {
2781 print "build '", $opts{'solution_file'}, "' using the '",
2782 ($opts{'optimize'} ? 'Release' : 'Debug'), "' configuration";
2785 print " to compile ",
2786 ($cloned_build ? "\nthe host and target builds of " : ''),
2787 "OpenDDS", (($buildtao || $cloned_build) ? ' and ACE+TAO' : '') ,".\n",
2788 "You can use the generated setenv.$specific{'ext'} script ",
2789 ($cloned_build ?
2790 "in the\nbuild${slash}host and build${slash}target dirs " : ''),
2791 "to set environment\nvariables for future shell sessions.\n";
2792 if ($is_windows && !$cross_compile) {
2793 print "Start Visual Studio from this command prompt so that it inherits ",
2794 "the correct\nenvironment variables. Try running \"devenv $opts{'solution_file'}\".\n";
2797 sub write_cmake_file {
2798 my $buildEnv = shift();
2800 my $module_dir = "$buildEnv->{'DDS_ROOT'}/cmake";
2801 mkpath($module_dir) unless -d $module_dir;
2802 my $fh = backup_and_open($module_dir . '/config.cmake');
2804 sub whack_to_slash {
2805 my $s = shift;
2806 $s =~ s|\\|/|g;
2807 return $s;
2810 sub to_cmake_scalar {
2811 my $val = shift;
2812 return 'ON' if "$val" eq '1';
2813 return 'OFF' if "$val" eq '0';
2814 return '"' . whack_to_slash($val) . '"';
2817 sub to_cmake_list {
2818 my @list = @{$_[0]};
2819 return '"' . join(';', map {whack_to_slash($_)} @list) . '"';
2822 sub to_cmake_value {
2823 my $value = shift();
2825 my $ref_type = ref($value);
2826 if ($ref_type eq 'ARRAY') {
2827 return to_cmake_list($value);
2829 elsif ($ref_type eq '') {
2830 return to_cmake_scalar($value);
2832 else {
2833 die "ERROR: to_cmake_value invalid value type, stopped";
2837 sub print_cmake_config {
2838 my $fh = shift();
2839 my $name = shift();
2840 my $value = shift();
2842 $name = uc("OPENDDS_$name");
2843 $name =~ s/-/_/g;
2845 print $fh "set($name ", to_cmake_value($value), ")\n";
2848 sub generate_config_cmake {
2849 my $fh = shift();
2850 my $buildEnv = shift();
2852 print_cmake_config($fh, 'MPC', Cwd::abs_path($buildEnv->{'MPC_ROOT'}));
2853 print_cmake_config($fh, 'ACE', Cwd::abs_path($buildEnv->{'ACE_ROOT'}));
2854 print_cmake_config($fh, 'TAO', Cwd::abs_path($buildEnv->{'TAO_ROOT'}));
2856 print $fh "\n# Based on MPC features\n";
2857 my @configs = (
2859 name => 'CXX11',
2860 feature => 'no_cxx11',
2861 inverted => 1,
2864 name => 'WCHAR',
2865 feature => 'uses_wchar',
2868 name => 'IPV6',
2869 feature => 'ipv6',
2872 name => 'SAFETY_PROFILE',
2873 feature => 'no_opendds_safety_profile',
2874 inverted => 1,
2877 name => 'VERSIONED_NAMESPACE',
2878 feature => 'versioned_namespace',
2881 name => 'SUPPRESS_ANYS',
2882 feature => 'dds_suppress_anys',
2883 default => 1,
2886 name => 'COVERAGE',
2887 feature => 'dds_non_coverage',
2888 inverted => 1,
2891 name => 'TAO_CORBA_E_COMPACT',
2892 feature => 'corba_e_compact',
2895 name => 'TAO_CORBA_E_MICRO',
2896 feature => 'corba_e_micro',
2899 name => 'TAO_MINIMUM_CORBA',
2900 feature => 'minimum_corba',
2903 name => 'TAO_IIOP',
2904 feature => 'tao_no_iiop',
2905 inverted => 1,
2906 default => 1,
2909 name => 'TAO_GEN_OSTREAM',
2910 feature => 'gen_ostream',
2913 name => 'TAO_OPTIMIZE_COLLOCATED_INVOCATIONS',
2914 feature => 'optimize_collocated_invocations',
2915 default => 1,
2918 my %features = get_features($buildEnv);
2919 for my $config (@configs) {
2920 my $inverted = $config->{inverted} // 0;
2921 my $default = $config->{default} // 0;
2922 my $enabled;
2923 if (exists($features{$config->{feature}})) {
2924 $enabled = $features{$config->{feature}} eq "1" ? !$inverted : $inverted;
2926 else {
2927 $enabled = $default;
2929 print_cmake_config($fh, $config->{name}, $enabled);
2932 if (defined($cxx_std)) {
2933 print_cmake_config($fh, 'OPENDDS_CXX_STD', $cxx_std);
2936 # TODO(iguessthislldo): Move to a smarter system that can use existing
2937 # ACE/TAO and that works with cross compiling.
2938 print $fh "\n# Based on configure script options\n";
2939 my %opts_to_use = (
2940 'built-in-topics' => 1,
2941 'compiler' => '',
2942 'content-filtered-topic' => 1,
2943 'content-subscription' => 1,
2944 'debug' => 1,
2945 'gtest' => '',
2946 'inline' => 1,
2947 'java' => '',
2948 'multi-topic' => 1,
2949 'object-model-profile' => 1,
2950 'openssl' => '',
2951 'ownership-kind-exclusive' => 1,
2952 'ownership-profile' => 1,
2953 'persistence-profile' => 1,
2954 'qt' => '',
2955 'query-condition' => 1,
2956 'rapidjson' => '',
2957 'security' => 0,
2958 'static' => 0,
2959 'std' => '',
2960 'xerces3' => '',
2962 for my $opt (sort(keys(%opts_to_use))) {
2963 print_cmake_config($fh, $opt, exists($opts{$opt}) ? $opts{$opt} : $opts_to_use{$opt});
2965 print_cmake_config($fh, 'features', \@features);
2966 print_cmake_config($fh, 'mpcopts', \@mpcopts);
2968 print $fh "\n# Sanitizers\n";
2969 for my $name (sort(keys(%all_sanitizers))) {
2970 print_cmake_config($fh, $name, exists($enabled_sanitizers{$name}) ? 1 : 0);
2972 if (scalar(@fsanitize)) {
2973 print_cmake_config($fh, 'SANITIZER_COMPILER_ARGS', \@sanitizer_compiler_args);
2974 print_cmake_config($fh, 'SANITIZER_LINKER_ARGS', \@sanitizer_linker_args);
2978 print $fh <<"EOF";
2979 # Contains configuration variables for the FindOpenDDS cmake module based on
2980 # detected defaults and/or user-supplied switches. Feel free to edit them as
2981 # necessary, but keep in mind that they will be moved to config.cmake.bak.*
2982 # the next time the \$DDS_ROOT/configure script is executed.
2984 # THIS FILE WAS AUTO-GENERATED BY: $0 @ARGS
2987 generate_config_cmake($fh, $buildEnv);
2988 dump_and_unlink($fh) if $opts{'dry-run'};