summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorocielliottc <elliottc@objectcomputing.com>2023-01-03 07:37:33 -0600
committerGitHub <noreply@github.com>2023-01-03 07:37:33 -0600
commit60b0c90efa801c758911bdc4c7949db6809eb3f4 (patch)
tree5f67f61f50f045f374f8e0ab4067888da95b92af
parent5aad93c4292b7aa33395dd2bb96c5599c73bdf5a (diff)
parent26e3b92f9ed15c69f17baca125444b886b874055 (diff)
downloadMPC-60b0c90efa801c758911bdc4c7949db6809eb3f4.tar.gz
Merge pull request #164 from DOCGroup/elliottc/cmake-support
Elliottc/cmake support
-rw-r--r--config/openssl.mpb15
-rw-r--r--config/xerces.mpb11
-rw-r--r--docs/USAGE8
-rw-r--r--docs/html/MakeProjectCreator.html45
-rw-r--r--docs/templates/cmake.txt17
-rw-r--r--modules/CMakeProjectCreator.pm117
-rw-r--r--modules/CMakeWorkspaceCreator.pm195
-rw-r--r--modules/Creator.pm2
-rw-r--r--modules/DirectoryManager.pm24
-rw-r--r--modules/ProjectCreator.pm23
-rw-r--r--modules/StringProcessor.pm12
-rw-r--r--modules/TemplateParser.pm38
-rw-r--r--modules/WorkspaceCreator.pm10
-rw-r--r--templates/cmake.mpd187
-rw-r--r--templates/cmakedll.mpt18
-rw-r--r--templates/cmakeexe.mpt3
16 files changed, 700 insertions, 25 deletions
diff --git a/config/openssl.mpb b/config/openssl.mpb
index 8af0fef4..84e466fa 100644
--- a/config/openssl.mpb
+++ b/config/openssl.mpb
@@ -13,8 +13,10 @@ feature(openssl) {
$(SSL_ROOT)/lib
}
- includes += $(SSL_INCDIR)
- libpaths += $(SSL_LIBDIR)
+ specific(!cmake) {
+ includes += $(SSL_INCDIR)
+ libpaths += $(SSL_LIBDIR)
+ }
specific(prop:windows) {
lit_libs += libeay32 ssleay32
@@ -36,6 +38,15 @@ feature(openssl) {
specific(prop:microsoft) {
libpaths += $(SSL_ROOT)/lib/VC
}
+
+ specific(cmake) {
+ // Undo the else of the !prop:windows above.
+ lit_libs -= ssl crypto
+ includes -= /usr/kerberos/include
+
+ lit_libs += ${OPENSSL_LIBRARIES}
+ includes += ${OPENSSL_INCLUDE_DIR}
+ }
}
feature(openssl11) {
diff --git a/config/xerces.mpb b/config/xerces.mpb
index 85f7e8aa..bfbd0d19 100644
--- a/config/xerces.mpb
+++ b/config/xerces.mpb
@@ -38,9 +38,6 @@ feature(xerces3) {
$(XERCESCROOT)/lib
}
- includes += $(XERCESC_INCDIR)
- libpaths += $(XERCESC_LIBDIR)
-
specific(prop:microsoft) {
xerceslib = xerces-c_3
@@ -57,6 +54,14 @@ feature(xerces3) {
xerceslib = xerces-c
}
+ specific(cmake) {
+ includes += ${XercesC_INCLUDE_DIRS}
+ xerceslib = ${XercesC_LIBRARIES}
+ } else {
+ includes += $(XERCESC_INCDIR)
+ libpaths += $(XERCESC_LIBDIR)
+ }
+
// We have to use lit_libs here as the library decorator
// does not necessarily match what MPC uses (particularly for
// static builds).
diff --git a/docs/USAGE b/docs/USAGE
index 49b3c5d1..47eac0ba 100644
--- a/docs/USAGE
+++ b/docs/USAGE
@@ -24,10 +24,10 @@ Usage: mwc.pl [-global <file>] [-include <directory>] [-recurse]
[-workers <#>] [-workers_dir <dir> | -workers_port <#>]
[-language <cplusplus | csharp | java | vb>]
[-type <automake | bcb2007 | bcb2009 | bds4 | bmake | cc | cdt6 |
- cdt7 | em3 | ghs | html | iar | make | nmake | rpmspec |
- sle | uvis | vc6 | vc7 | vc71 | vc8 | vc9 | vc10 | vc11 |
- vc12 | vc14 | vs2017 | vs2019 | vs2022 | wb26 | wb30 |
- wix>]
+ cdt7 | cmake | em3 | ghs | html | iar | make | nmake |
+ rpmspec | sle | uvis | vc6 | vc7 | vc8 | vc9 | vc10 |
+ vc11 | vc12 | vc14 | vc71 | vs2017 | vs2019 | vs2022 |
+ wb26 | wb30 | wix>]
[files]
-base Add <project> as a base project to each generated
diff --git a/docs/html/MakeProjectCreator.html b/docs/html/MakeProjectCreator.html
index 6e59a72b..bede62d4 100644
--- a/docs/html/MakeProjectCreator.html
+++ b/docs/html/MakeProjectCreator.html
@@ -286,6 +286,18 @@
<tr>
<td rowspan="1" colspan="1">
<p class="Tbl-Body">
+ <em class="TableCode">cmake</em>
+ </p>
+ </td>
+
+ <td rowspan="1" colspan="1">
+ <p class="Tbl-Body">Support for CMake requires user provided modules for custom commands.</p>
+ </td>
+ </tr>
+
+ <tr>
+ <td rowspan="1" colspan="1">
+ <p class="Tbl-Body">
<em class="TableCode">em3</em>
</p>
</td>
@@ -6782,7 +6794,7 @@ class="Code">specific</em> clause.
</blockquote>
<p class="Body">
- The following mpc file (<em class=
+ The following .mpc file (<em class=
"Code">RTEC_Perf.mpc</em> ) shows the simple and small number of
lines required to generate usable build tool project files.
</p>
@@ -6899,7 +6911,9 @@ class="Code">specific</em> clause.
Line five adds <em class=
"Code">TAO_RTEC_PERF_BUILD_DLL</em> to the <em class=
"Code">dllflags</em>, which defines a macro that is used by the
- <em class="Code">rtec_perf_export.h</em> header file.
+ <em class="Code">rtec_perf_export.h</em> header file when
+ building shared libraries on platforms that support symbol
+ visibility.
</p>
<blockquote>
@@ -8174,7 +8188,7 @@ class="Code">specific</em> clause.
<blockquote>
- <p class="Code">document_template.pl v1.3</p>
+ <p class="Code">document_template.pl v1.4</p>
<p class="Code">
Usage: document_template.pl &lt;template&gt;
@@ -8184,6 +8198,16 @@ class="Code">specific</em> clause.
<p class="Code">&nbsp;</p>
<p class="Code">
+ template &nbsp;&nbsp;&nbsp;- .mpd file to document. Certain MPC types don't
+ use a template,
+ </p>
+
+ <p class="Code">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in that case this aregument can be the Perl module.
+ </p>
+
+ <p class="Code">
html output - This defaults to the name of the
template file with the .mpd
</p>
@@ -8558,6 +8582,21 @@ class="Code">specific</em> clause.
</td>
</tr>
+ <tr>
+ <td rowspan="1" colspan="1">
+ <p class="Tbl-Body">
+ <em class=
+ "TableCode">command-&gt;type</em>
+ </p>
+ </td>
+
+ <td rowspan="1" colspan="1">
+ <p class="Tbl-Body">
+ The original type of file for this command.
+ </p>
+ </td>
+ </tr>
+
</table>
</p>
diff --git a/docs/templates/cmake.txt b/docs/templates/cmake.txt
new file mode 100644
index 00000000..c20cb054
--- /dev/null
+++ b/docs/templates/cmake.txt
@@ -0,0 +1,17 @@
+// Current as of 10/26/2022
+// This defines the role of all the template variables specific to the
+// 'cmake' project type.
+//
+// The value &#36; is used below to represent the dollar sign. The dollar
+// sign is interpreted by the parser in document_template.pl.
+//
+// Please try to keep this alphabetically sorted.
+//
+cmake_minimum_required = Sets the minimum required version for CMake.
+env_dllout = The value of 'dllout' with &#36;() values converted to &#36;ENV{}.
+env_exeout = The value of 'exeout' with &#36;() values converted to &#36;ENV{}.
+env_includes = The value of 'includes' with &#36;() values converted to &#36;ENV{}.
+env_libout = The value of 'libout' with &#36;() values converted to &#36;ENV{}.
+env_libpaths = The value of 'libpaths' with &#36;() values converted to &#36;ENV{}.
+packages = A list of packages to be used with the find_package() command.
+pre_find_package = A specific command or set of commands to be issued before any find_package() commands are called.
diff --git a/modules/CMakeProjectCreator.pm b/modules/CMakeProjectCreator.pm
new file mode 100644
index 00000000..e1896dc4
--- /dev/null
+++ b/modules/CMakeProjectCreator.pm
@@ -0,0 +1,117 @@
+package CMakeProjectCreator;
+
+# ************************************************************
+# Description : A CMake Project Creator
+# Author : Chad Elliott
+# Create Date : 10/10/2022
+# ************************************************************
+
+# ************************************************************
+# Pragmas
+# ************************************************************
+
+use strict;
+
+use ProjectCreator;
+
+use vars qw(@ISA);
+@ISA = qw(ProjectCreator);
+
+# ************************************************************
+# Subroutine Section
+# ************************************************************
+
+sub pre_generation {
+ my $self = shift;
+
+ ## For CMake, we are expecting a hybrid of custom types and modules.
+ ## We are turning off all automatic output so that the modules defined
+ ## for CMake can handle these artifacts.
+ foreach my $gentype (keys %{$self->{'generated_exts'}}) {
+ $self->{'generated_exts'}->{$gentype}->{'automatic_out'} = 0;
+ }
+}
+
+sub default_to_library {
+ ## In case there are only generated source files...
+ return 1;
+}
+
+sub need_to_write_project {
+ my $self = shift;
+
+ ## Because we do not automatically add custom output, it is possible that
+ ## the project only has generated source files and expects them to cause
+ ## an automatic library name to be chosen. If the base
+ ## need_to_write_project() tells us that it's only generated source files
+ ## but the user didn't mark this project as "custom only", then we have to
+ ## override it back to 1 to retain the user provided target name.
+ my $status = $self->SUPER::need_to_write_project();
+ if ($status == 2 && !$self->get_assignment('custom_only')) {
+ $status = 1;
+ }
+
+ return $status;
+}
+
+sub get_use_env {
+ ## Override the option getter so that, for CMake, MPC always functions as
+ ## if the -use_env option was supplied on the command line.
+ return 1;
+}
+
+sub pre_write_output_file {
+ my $self = shift;
+ return $self->combine_custom_types();
+}
+
+sub dollar_special {
+ return 1;
+}
+
+sub project_file_prefix {
+ return "CMakeLists.";
+}
+
+sub escape_spaces {
+ #my $self = shift;
+ return 1;
+}
+
+sub get_dll_exe_template_input_file {
+ return 'cmakeexe';
+}
+
+sub get_dll_template_input_file {
+ return 'cmakedll';
+}
+
+sub fill_value {
+ my($self, $name) = @_;
+
+ if ($name eq 'language') {
+ ## Currently, we only support C++
+ return 'CXX' if ($self->get_language() eq Creator::cplusplus());
+ }
+ elsif ($name =~ /^env_(\w+)/) {
+ my $dotdir = '${CMAKE_CURRENT_SOURCE_DIR}' .
+ ($1 eq 'libpaths' ? ' ${CMAKE_CURRENT_BINARY_DIR}' : '');
+ my $paths = $self->get_assignment($1);
+ if (defined $paths) {
+ $paths = $self->create_array($paths);
+ foreach my $path (@$paths) {
+ if ($path eq '.') {
+ $path = $dotdir;
+ }
+ else {
+ $path =~ s/\$\(([^\)]+)\)/\${$1}/g;
+ }
+ }
+ return "@$paths";
+ }
+ }
+
+ return undef;
+}
+
+1;
diff --git a/modules/CMakeWorkspaceCreator.pm b/modules/CMakeWorkspaceCreator.pm
new file mode 100644
index 00000000..f5d9e63a
--- /dev/null
+++ b/modules/CMakeWorkspaceCreator.pm
@@ -0,0 +1,195 @@
+package CMakeWorkspaceCreator;
+
+# ************************************************************
+# Description : A CMake Workspace creator
+# Author : Chad Elliott
+# Create Date : 10/10/2022
+# ************************************************************
+
+# ************************************************************
+# Pragmas
+# ************************************************************
+
+use strict;
+use File::Basename;
+
+use CMakeProjectCreator;
+use WorkspaceCreator;
+
+use vars qw(@ISA);
+@ISA = qw(WorkspaceCreator);
+
+# ************************************************************
+# Data Section
+# ************************************************************
+
+my $version = '3.12.0';
+
+# ************************************************************
+# Subroutine Section
+# ************************************************************
+
+sub workspace_per_project {
+ #my $self = shift;
+ return 1;
+}
+
+sub workspace_file_name {
+ return 'CMakeLists.txt';
+}
+
+sub pre_workspace {
+ my($self, $fh) = @_;
+ my $crlf = $self->crlf();
+
+ $self->print_workspace_comment($fh,
+ '# CMake Workspace', $crlf,
+ '#', $crlf,
+ '# This file was generated by MPC.', $crlf,
+ '#', $crlf,
+ '# MPC Command:', $crlf,
+ '# ', $self->create_command_line_string($0, @ARGV), $crlf, $crlf);
+}
+
+sub out_of_tree {
+ my($self, $dir) = @_;
+ return ($dir =~ /^\.\.\// || !$self->path_is_relative($dir));
+}
+
+## Get the top level directory in the path. If the path does not contain
+## a directory, the path will be returned unmodified.
+sub get_top_directory {
+ my($self, $path) = @_;
+
+ ## First, convert the path to a relative path based on the current working
+ ## directory.
+ my $dir = $self->path_to_relative($self->getcwd(), $path);
+ if ($self->out_of_tree($dir)) {
+ ## If the directory is above the current directory or not relative to
+ ## the current working directory, we need to give the directory portion
+ ## back and call it a day.
+ return $self->mpc_dirname($dir);
+ }
+ else {
+ my $done = 0;
+ do {
+ ## Go up one directory. If we were already at the top directory,
+ ## we're finished.
+ my $next = $self->mpc_dirname($dir);
+ if ($next eq '.') {
+ $done = 1;
+ }
+ else {
+ $dir = $next;
+ }
+ } while(!$done);
+ }
+
+ return $dir;
+}
+
+sub write_ws_top {
+ my($self, $fh, $ws) = @_;
+ my $crlf = $self->crlf();
+ print $fh "cmake_minimum_required(VERSION $version)", $crlf,
+ "project($ws CXX)", $crlf;
+}
+
+sub write_include_ws {
+ my($self, $prjs) = @_;
+ my $fh = new FileHandle();
+ my $dir = $self->mpc_dirname($$prjs[0]);
+ my $file = $dir . '/' . $self->workspace_file_name();
+
+ if (open($fh, ">$file")) {
+ my $crlf = $self->crlf();
+ $self->pre_workspace($fh);
+ $self->write_ws_top($fh, basename($dir));
+ foreach my $prj (@$prjs) {
+ print $fh "${crlf}include(", basename($prj), ")";
+ }
+ print $fh $crlf;
+ close($fh);
+ }
+}
+
+sub write_comps {
+ my($self, $fh, $creator) = @_;
+ my $status = 1;
+ my $errorString = '';
+ my @project_dirs;
+ my @projects = $self->sort_dependencies($self->get_projects(), 0);
+
+ ## Build a list of top level directories. We only want to go down one
+ ## directory. The workspace in that directory will handle going to
+ ## other subdirectories.
+ my %dirs;
+ my %out_of_tree;
+ foreach my $entry (@projects) {
+ my $dir = $self->get_top_directory($entry);
+ if ($dir ne $entry) {
+ if (!exists $dirs{$dir}) {
+ ## Keep track of the project existing in this directory
+ $dirs{$dir} = 1;
+
+ push(@project_dirs, $dir);
+ }
+
+ ## If this directory is out-of-tree, it will not contain a top-level
+ ## workspace (due to the way that workspace-per-directory works). We
+ ## need to keep track of it here.
+ if ($self->out_of_tree($dir)) {
+ if (exists $out_of_tree{$dir}) {
+ push(@{$out_of_tree{$dir}}, $entry);
+ }
+ else {
+ $out_of_tree{$dir} = [$entry];
+ }
+ }
+ }
+ }
+
+ ## Create the basis of a project so that we can add our add_subdirectory()
+ ## calls below it.
+ my $crlf = $self->crlf();
+ my $ws = TemplateParser::actual_normalize(undef, $self->get_workspace_name());
+ $self->write_ws_top($fh, $ws);
+
+ my $first = 1;
+ my %bin_used;
+ foreach my $dir (@project_dirs) {
+ if ($first) {
+ $first = undef;
+ print $fh $crlf;
+ }
+ my $bin_dir = '';
+ if (exists $out_of_tree{$dir}) {
+ ## Because this directory is out-of-tree, CMake requires a binary
+ ## directory to be passed to add_subdirectory().
+ my $bin = basename($dir);
+ while(exists $bin_used{$bin}) {
+ $bin .= '_';
+ }
+ $bin_dir = " $bin";
+ $bin_used{$bin} = 1;
+ $self->write_include_ws($out_of_tree{$dir});
+ }
+ print $fh "add_subdirectory($dir$bin_dir)$crlf";
+ }
+
+ $first = 1;
+ foreach my $entry (@projects) {
+ my $dir = $self->mpc_dirname($entry);
+ if ($dir eq '.') {
+ if ($first) {
+ $first = undef;
+ print $fh $crlf;
+ }
+ print $fh "include($entry)$crlf";
+ }
+ }
+
+ return $status, $errorString;
+}
+
+1;
diff --git a/modules/Creator.pm b/modules/Creator.pm
index 1d70dabe..00c64eac 100644
--- a/modules/Creator.pm
+++ b/modules/Creator.pm
@@ -1327,7 +1327,7 @@ sub get_initial_relative_values {
sub get_secondary_relative_values {
my $self = shift;
- return ($self->{'use_env'} ? \%ENV :
+ return ($self->get_use_env() ? \%ENV :
$self->{'relative'}), $self->{'expand_vars'};
}
diff --git a/modules/DirectoryManager.pm b/modules/DirectoryManager.pm
index 6dfdd92d..bdc23151 100644
--- a/modules/DirectoryManager.pm
+++ b/modules/DirectoryManager.pm
@@ -199,6 +199,30 @@ sub path_is_relative {
return (index($_[1], '/') != 0 && $_[1] !~ /^[A-Z]:[\/\\]/i && $_[1] !~ /^\$\(\w+\)/);
}
+sub path_to_relative {
+ my($self, $check, $path) = @_;
+
+ ## See if it's already relative. If it is, there's nothing to do.
+ if ($path !~ s/^.[\/]+// && !$self->path_is_relative($path)) {
+ ## See how many times we have to chop off a directory until we find that
+ ## the provided path contains part of the current working directory.
+ my $dircount = 0;
+ while($check ne '.' && index($path, $check) != 0) {
+ $dircount++;
+ $check = $self->mpc_dirname($check);
+ }
+
+ ## If we didn't go all the way back up the current working directory, we
+ ## can create a relative path from it based on the number of directories
+ ## we removed above.
+ if ($check ne '.') {
+ $path = ('../' x $dircount) . substr($path, length($check) + 1);
+ }
+ }
+
+ return $path;
+}
+
# ************************************************************
# Virtual Methods To Be Overridden
# ************************************************************
diff --git a/modules/ProjectCreator.pm b/modules/ProjectCreator.pm
index 5ebbbd0e..5776ea72 100644
--- a/modules/ProjectCreator.pm
+++ b/modules/ProjectCreator.pm
@@ -835,6 +835,9 @@ sub parse_line {
## Project Ending
if (!defined $self->{'reading_parent'}->[0] &&
!$self->{'reading_global'}) {
+ ## Call into the project type's pre-generation hook.
+ $self->pre_generation();
+
## Fill in all the default values
$self->generate_defaults();
@@ -2862,7 +2865,7 @@ sub generate_default_target_names {
if (!defined $sources[0]) {
@sources = $self->get_component_list($self->get_resource_tag(), 1);
}
- if (defined $sources[0]) {
+ if (defined $sources[0] || $self->default_to_library()) {
if (!$shared_empty) {
$self->process_assignment('sharedname',
$self->{'unmodified_project_name'});
@@ -3347,7 +3350,7 @@ sub remove_duplicated_files {
sub generated_source_listed {
- my($self, $gent, $tag, $arr, $sext) = @_;
+ my($self, $gent, $tag, $arr) = @_;
my $names = $self->{$tag};
## Find out which generated source files are listed
@@ -3417,9 +3420,7 @@ sub list_default_generated {
UNIVERSAL::isa($self->{'special_supplied'}->{$type}, 'ARRAY'))) &&
(!defined $self->{'generated_exts'}->{$type} ||
$self->{'generated_exts'}->{$type}->{'automatic_in'})) {
- if (!$self->generated_source_listed(
- $gentype, $type, \%arr,
- $self->{'valid_components'}->{$gentype})) {
+ if (!$self->generated_source_listed($gentype, $type, \%arr)) {
$self->add_generated_files($gentype, $type, $group, \%arr);
}
}
@@ -4679,9 +4680,9 @@ sub get_custom_value {
## have extraneous data associated with commands from previous iterations.
$self->{'custom_multi_details'} = {};
- my %details = ('flags' => 'commandflags',
+ my %details = ('flags' => 'commandflags',
'outopt' => 'output_option',
- 'gdir' => 'gendir');
+ 'gdir' => 'gendir');
for my $tag (@{$self->{'custom_multi_cmd'}->{$based}}) {
my $command = $self->get_custom_assign_or_override('command', $tag,
$based, @params);
@@ -6127,4 +6128,12 @@ sub pre_write_output_file {
return 1;
}
+sub pre_generation {
+ #my $self = shift;
+}
+
+sub default_to_library {
+ return 0;
+}
+
1;
diff --git a/modules/StringProcessor.pm b/modules/StringProcessor.pm
index f6da1934..3d71e951 100644
--- a/modules/StringProcessor.pm
+++ b/modules/StringProcessor.pm
@@ -52,12 +52,16 @@ sub process_special {
my $escaped = ($line =~ s/\\\\/\01/g);
$escaped |= ($line =~ s/\\"/\02/g);
- ## Un-escape all other characters
- $line =~ s/\\(.)/$1/g;
-
## Remove any non-escaped double quotes
$line =~ s/"//g;
+ ## Un-escape all other characters. Using eval allows the user to pass
+ ## escaped characters that will be converted to their actual character
+ ## couterpart (i.e., \n, \f, etc).
+ if (index($line, '\\') != -1) {
+ eval("\$line = \"$line\"");
+ }
+
## Put the escaped double quotes and backslashes back in
if ($escaped) {
$line =~ s/\02/"/g;
@@ -82,6 +86,7 @@ sub create_array {
$escaped |= ($line =~ s/\\\t/\04/g);
$escaped |= ($line =~ s/\\\"/\05/g);
$escaped |= ($line =~ s/\\\\/\06/g);
+ $escaped |= ($line =~ s/\n/\07/g);
foreach my $part (grep(!/^\s*$/,
split(/(\"[^\"]+\"|\'[^\']+\'|\s+)/, $line))) {
@@ -98,6 +103,7 @@ sub create_array {
$part =~ s/\04/\t/g;
$part =~ s/\05/\"/g;
$part =~ s/\06/\\/g;
+ $part =~ s/\07/\n/g;
}
## Push it onto the array
diff --git a/modules/TemplateParser.pm b/modules/TemplateParser.pm
index f8fddaef..d2dd92e6 100644
--- a/modules/TemplateParser.pm
+++ b/modules/TemplateParser.pm
@@ -69,6 +69,7 @@ my %keywords = ('if' => 0,
'reverse' => $get_type|$perform_type,
'sort' => $get_type|$perform_type,
'uniq' => $get_type|$perform_type,
+ 'cmake_macro' => $get_type|$perform_type|$doif_type,
'multiple' => $get_type|$doif_type|$get_combined_type,
'starts_with' => $get_type|$doif_type|$get_combined_type,
'ends_with' => $get_type|$doif_type|$get_combined_type,
@@ -1721,6 +1722,43 @@ sub handle_basename {
}
+sub actual_cmake_macro {
+ my($self, $value) = @_;
+ return $1 if ($value =~ /^\$\{(\w+)\}$/);
+ return '';
+}
+
+
+sub perform_cmake_macro {
+ my($self, $value) = @_;
+ my @val;
+ foreach my $val (@$value) {
+ push(@val, $self->actual_cmake_macro($val));
+ }
+ return @val;
+}
+
+
+sub get_cmake_macro {
+ my($self, $name) = @_;
+ return $self->actual_cmake_macro($self->get_value_with_default($name));
+}
+
+
+sub doif_cmake_macro {
+ my($self, $value) = @_;
+ return (defined $value && $value ne '');
+}
+
+
+sub handle_cmake_macro {
+ my($self, $name) = @_;
+
+ $self->append_current(
+ $self->actual_cmake_macro($self->get_value_with_default($name)));
+}
+
+
sub handle_basenoextension {
my($self, $name) = @_;
my $val = $self->tp_basename($self->get_value_with_default($name));
diff --git a/modules/WorkspaceCreator.pm b/modules/WorkspaceCreator.pm
index b6be9879..b8b993f8 100644
--- a/modules/WorkspaceCreator.pm
+++ b/modules/WorkspaceCreator.pm
@@ -1343,8 +1343,14 @@ sub generate_hierarchy {
my %projinfo = %{$originfo};
foreach my $prj (@projects) {
- my($top, $rest) = $self->topname($prj);
-
+ ## If the project path starts with ./ the code assumed that the top was
+ ## the current directory and would end up not creating the workspace as
+ ## it should have been. We will clean up the project directory and pass
+ ## that to topname() instead.
+ my $clean = $prj;
+ $clean =~ s/^\.[\/]+//;
+
+ my($top, $rest) = $self->topname($clean);
if (!defined $current) {
$current = $top;
push(@saved, $rest);
diff --git a/templates/cmake.mpd b/templates/cmake.mpd
new file mode 100644
index 00000000..9e39dc43
--- /dev/null
+++ b/templates/cmake.mpd
@@ -0,0 +1,187 @@
+cmake_minimum_required(VERSION <%cmake_minimum_required(3.12.0)%>)
+
+<%marker(top)%>
+project(<%project_name%> <%language%>)
+option(BUILD_SHARED_LIBS "Build using shared libraries" <%if(need_staticflags)%>OFF<%else%>ON<%endif%>)
+
+<%if(pre_find_package)%>
+<%pre_find_package%>
+<%endif%>
+<%foreach(packages)%>
+find_package(<%package%> REQUIRED)
+<%endfor%>
+
+<%if(header_files)%>
+set(HEADER_FILES_<%uc(normalize(project_name))%> <%header_files%>)
+
+<%endif%>
+<%if(inline_files)%>
+set(INLINE_FILES_<%uc(normalize(project_name))%> <%inline_files%>)
+
+<%endif%>
+<%if(template_files)%>
+set(TEMPLATE_FILES_<%uc(normalize(project_name))%> <%template_files%>)
+
+<%endif%>
+set(SOURCE_FILES_<%uc(normalize(project_name))%> <%source_files%>)
+<%if(libs || lit_libs || pure_libs)%>
+<%foreach(libs)%>
+<%if(cmake_macro(lib))%>
+if(<%cmake_macro(lib)%>)
+ set(<%cmake_macro(lib)%>_DEFINED TRUE)
+endif()
+<%endif%>
+<%endfor%>
+if(CMAKE_CONFIGURATION_TYPES)
+set(TARGET_LINK_LIBRARIES_<%uc(normalize(project_name))%><%if(libs)%> <%foreach(configurations)%><%fornotfirst(" ")%>$<$<CONFIG:<%configuration%>>:<%foreach(libs)%><%fornotfirst(" ")%><%if(cmake_macro(lib))%>$<IF:$<BOOL:${<%cmake_macro(lib)%>_DEFINED}>,<%lib%>${LIBRARY_DECORATOR}<%lib_modifier%>,><%else%><%lib%>${LIBRARY_DECORATOR}<%lib_modifier%><%endif%><%endfor%>><%fornotlast("\n")%><%endfor%><%endif%><%if(lit_libs)%> <%lit_libs%><%endif%><%if(pure_libs)%> <%pure_libs%><%endif%>)
+else()
+set(TARGET_LINK_LIBRARIES_<%uc(normalize(project_name))%><%if(libs)%><%foreach(libs)%> <%if(cmake_macro(lib))%>$<IF:$<BOOL:${<%cmake_macro(lib)%>_DEFINED}>,<%lib%>${LIBRARY_DECORATOR},><%else%><%lib%>${LIBRARY_DECORATOR}<%endif%><%endfor%><%endif%><%if(lit_libs)%> <%lit_libs%><%endif%><%if(pure_libs)%> <%pure_libs%><%endif%>)
+endif()
+<%endif%>
+set(PROJECT_TARGET_<%uc(normalize(project_name))%> <%if(exename)%><%exename%><%else%><%if(sharedname)%><%sharedname%>${LIBRARY_DECORATOR}<%else%><%if(staticname)%><%staticname%>${LIBRARY_DECORATOR}<%else%><%project_name%>${LIBRARY_DECORATOR}<%endif%><%endif%><%endif%>)
+<%marker(macros)%>
+
+<%if(exeout)%>
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY <%env_exeout%>)
+<%if(use_lib_modifier)%>
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG <%env_exeout%>)
+<%endif%>
+<%endif%>
+<%if(!exename)%>
+<%if(libout)%>
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY <%env_libout%>)
+<%if(use_lib_modifier)%>
+<%foreach(configurations)%>
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_<%uc(configuration)%> <%env_libout%>)
+<%endfor%>
+<%endif%>
+<%endif%>
+<%if(dllout)%>
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY <%env_dllout%>)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY <%env_dllout%>)
+<%if(use_lib_modifier)%>
+<%foreach(configurations)%>
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_<%uc(configuration)%> <%env_dllout%>)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_<%uc(configuration)%> <%env_dllout%>)
+<%endfor%>
+<%endif%>
+<%else%>
+<%if(libout)%>
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY <%env_libout%>)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY <%env_libout%>)
+<%if(use_lib_modifier)%>
+<%foreach(configurations)%>
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_<%uc(configuration)%> <%env_libout%>)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_<%uc(configuration)%> <%env_libout%>)
+<%endfor%>
+<%endif%>
+<%endif%>
+<%endif%>
+<%endif%>
+
+<%if(compile_flags)%>
+target_compile_options(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PUBLIC <%compile_flags%>)
+
+<%endif%>
+<%if(exename)%>
+add_executable(${PROJECT_TARGET_<%uc(normalize(project_name))%>} ${SOURCE_FILES_<%uc(normalize(project_name))%>})
+target_link_libraries(${PROJECT_TARGET_<%uc(normalize(project_name))%>} ${TARGET_LINK_LIBRARIES_<%uc(normalize(project_name))%>})
+<%if(staticflags)%>
+if(NOT BUILD_SHARED_LIBS)
+ target_compile_definitions(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PRIVATE <%staticflags%>)
+ set_property(TARGET ${PROJECT_TARGET_<%uc(normalize(project_name))%>} PROPERTY
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+endif()
+<%endif%>
+<%else%>
+<%if(sharedname)%>
+add_library(${PROJECT_TARGET_<%uc(normalize(project_name))%>} ${SOURCE_FILES_<%uc(normalize(project_name))%>})
+<%if(dynamicflags)%>
+if(BUILD_SHARED_LIBS)
+ target_compile_definitions(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PRIVATE <%dynamicflags%>)
+<%if(staticflags)%>
+else()
+ target_compile_definitions(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PRIVATE <%staticflags%>)
+<%endif%>
+endif()
+<%else%>
+<%if(staticflags)%>
+if(NOT BUILD_SHARED_LIBS)
+ target_compile_definitions(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PRIVATE <%staticflags%>)
+endif()
+<%endif%>
+<%endif%>
+target_link_libraries(${PROJECT_TARGET_<%uc(normalize(project_name))%>} ${TARGET_LINK_LIBRARIES_<%uc(normalize(project_name))%>})
+<%else%>
+<%if(staticname)%>
+add_library(${PROJECT_TARGET_<%uc(normalize(project_name))%>} ${SOURCE_FILES_<%uc(normalize(project_name))%>})
+<%if(staticflags)%>
+target_compile_definitions(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PRIVATE <%staticflags%>)
+<%endif%>
+<%endif%>
+<%endif%>
+<%if(use_lib_modifier)%>
+<%foreach(configurations)%>
+<%if(lib_modifier)%>
+set_target_properties(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PROPERTIES <%uc(configuration)%>_POSTFIX "<%lib_modifier%>")
+<%endif%>
+<%endfor%>
+<%endif%>
+<%endif%>
+
+if(CMAKE_CONFIGURATION_TYPES)
+target_compile_definitions(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PUBLIC
+<%foreach(configurations)%> $<$<CONFIG:<%configuration%>>:MPC_LIB_MODIFIER="${LIBRARY_DECORATOR}<%lib_modifier%>"><%fornotlast("\n")%><%endfor%>)
+endif()
+
+<%if(includes)%>
+target_include_directories(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PRIVATE <%env_includes%>)
+
+<%endif%>
+<%if(libpaths)%>
+target_link_directories(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PRIVATE <%env_libpaths%>)
+
+<%endif%>
+<%if(pch_header)%>
+target_precompile_headers(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PRIVATE <%pch_header%>)
+
+<%endif%>
+<%if(pch_source)%>
+target_compile_definitions(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PUBLIC <%pch_defines%>)
+<%endif%>
+<%if(macros)%>
+target_compile_definitions(${PROJECT_TARGET_<%uc(normalize(project_name))%>} PUBLIC <%macros%>)
+
+<%endif%>
+<%if(custom_types)%>
+<%foreach(custom_types)%>
+<%if(custom_type->input_files)%>
+<%if(custom_type->command)%>
+
+include(<%custom_type%> OPTIONAL)
+<%foreach(custom_type->input_files)%>
+<%uc(custom_type)%>_TARGET_SOURCES(
+ ${PROJECT_TARGET_<%uc(normalize(project_name))%>} PUBLIC <%custom_type->input_file%>
+ <%uc(custom_type)%>_OPTIONS <%if(flag_overrides(custom_type->input_file, commandflags))%><%flag_overrides(custom_type->input_file, commandflags)%><%else%><%custom_type->commandflags%><%endif%><%if(custom_type->output_option)%> <%custom_type->output_option%> <%if(flag_overrides(custom_type->input_file, gendir))%><%flag_overrides(custom_type->input_file, gendir)%>/<%basename(custom_type->input_file->output_file)%><%else%><%custom_type->input_file->output_file%><%endif%><%endif%>)
+
+<%endfor%>
+<%else%>
+<%foreach(custom_type->input_files)%>
+<%if(custom_type->input_file->commands)%>
+<%if(forfirst)%>
+include(<%custom_type%> OPTIONAL)
+<%endif%>
+<%uc(custom_type)%>_TARGET_SOURCES(
+ ${PROJECT_TARGET_<%uc(normalize(project_name))%>} PUBLIC <%custom_type->input_file%>
+<%foreach(custom_type->input_file->commands)%>
+ <%uc(custom_type->input_file->command->type)%>_OPTIONS <%custom_type->input_file->command->flags%><%fornotlast("\n")%><%endfor%>)
+
+<%endif%>
+<%endfor%>
+<%endif%>
+<%endif%>
+<%endfor%>
+<%endif%>
+
+<%marker(local)%>
+<%marker(bottom)%>
diff --git a/templates/cmakedll.mpt b/templates/cmakedll.mpt
new file mode 100644
index 00000000..4b83cf64
--- /dev/null
+++ b/templates/cmakedll.mpt
@@ -0,0 +1,18 @@
+// -*- MPC -*-
+
+conditional_include "common"
+
+configurations = Debug Release
+
+// ***********************************************************************
+// Configuration Section
+// ***********************************************************************
+
+Debug {
+ lib_modifier = d
+}
+
+Release {
+}
+
+conditional_include "user_cmakedll"
diff --git a/templates/cmakeexe.mpt b/templates/cmakeexe.mpt
new file mode 100644
index 00000000..55c1608f
--- /dev/null
+++ b/templates/cmakeexe.mpt
@@ -0,0 +1,3 @@
+// -*- MPC -*-
+conditional_include "cmakedll"
+conditional_include "user_cmakeexe"