summaryrefslogtreecommitdiff
path: root/ACE/MPC/modules/ProjectCreator.pm
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/MPC/modules/ProjectCreator.pm')
-rw-r--r--ACE/MPC/modules/ProjectCreator.pm5425
1 files changed, 5425 insertions, 0 deletions
diff --git a/ACE/MPC/modules/ProjectCreator.pm b/ACE/MPC/modules/ProjectCreator.pm
new file mode 100644
index 00000000000..2f68c18183c
--- /dev/null
+++ b/ACE/MPC/modules/ProjectCreator.pm
@@ -0,0 +1,5425 @@
+package ProjectCreator;
+
+# ************************************************************
+# Description : Base class for all project creators
+# Author : Chad Elliott
+# Create Date : 3/13/2002
+# ************************************************************
+
+# ************************************************************
+# Pragmas
+# ************************************************************
+
+use strict;
+use FileHandle;
+use File::Path;
+
+use Creator;
+use TemplateInputReader;
+use TemplateParser;
+use FeatureParser;
+use CommandHelper;
+
+use vars qw(@ISA);
+@ISA = qw(Creator);
+
+# ************************************************************
+# Data Section
+# ************************************************************
+
+## The basic extensions known to a project creator
+my $BaseClassExtension = 'mpb';
+my $ProjectCreatorExtension = 'mpc';
+my $TemplateExtension = 'mpd';
+my $TemplateInputExtension = 'mpt';
+
+## This feature is enabled or disabled depending on whether
+## or not the -static option is used.
+my $static_libs_feature = 'static_libs_only';
+
+## Valid names for assignments within a project
+## Bit Meaning
+## 0 Preserve the order for additions (1) or invert it (0)
+## 1 Add this value to template input value (if there is one)
+## 2 Preserve <% %> settings for evaluation within the template
+my %validNames = ('after' => 1,
+ 'avoids' => 3,
+ 'custom_only' => 1,
+ 'dllout' => 1,
+ 'dynamicflags' => 3,
+ 'exename' => 1,
+ 'exeout' => 1,
+ 'includes' => 3,
+ 'libout' => 1,
+ 'libpaths' => 3,
+ 'libs' => 2,
+ 'lit_libs' => 2,
+ 'macros' => 3,
+ 'managed' => 1,
+ 'pch_header' => 1,
+ 'pch_source' => 1,
+ 'postbuild' => 5,
+ 'postclean' => 5,
+ 'prebuild' => 5,
+ 'pure_libs' => 2,
+ 'recurse' => 1,
+ 'recursive_includes' => 3,
+ 'recursive_libpaths' => 3,
+ 'requires' => 3,
+ 'sharedname' => 1,
+ 'staticflags' => 3,
+ 'staticname' => 1,
+ 'tagchecks' => 1,
+ 'tagname' => 1,
+ 'version' => 1,
+ 'webapp' => 1,
+ );
+
+## Custom definitions only
+## Bit Meaning
+## 0 Value is always an array
+## 1 Value is an array and name gets 'outputext' converted to 'files'
+## 2 Value is always scalar
+## 3 Name can also be used in an 'optional' clause
+## 4 Needs <%...%> conversion
+my %customDefined = ('automatic_in' => 0x04,
+ 'automatic_out' => 0x04,
+ 'command' => 0x14,
+ 'commandflags' => 0x14,
+ 'dependent' => 0x14,
+ 'precommand' => 0x14,
+ 'postcommand' => 0x14,
+ 'inputext' => 0x01,
+ 'libpath' => 0x04,
+ 'output_follows_input' => 0x04,
+ 'output_option' => 0x14,
+ 'pch_postrule' => 0x04,
+ 'pre_extension' => 0x08,
+ 'source_pre_extension' => 0x08,
+ 'template_pre_extension' => 0x08,
+ 'header_pre_extension' => 0x08,
+ 'inline_pre_extension' => 0x08,
+ 'documentation_pre_extension' => 0x08,
+ 'resource_pre_extension' => 0x08,
+ 'generic_pre_extension' => 0x08,
+ 'pre_filename' => 0x08,
+ 'source_pre_filename' => 0x08,
+ 'template_pre_filename' => 0x08,
+ 'header_pre_filename' => 0x08,
+ 'inline_pre_filename' => 0x08,
+ 'documentation_pre_filename' => 0x08,
+ 'resource_pre_filename' => 0x08,
+ 'generic_pre_filename' => 0x08,
+ 'pre_dirname' => 0x08,
+ 'source_pre_dirname' => 0x08,
+ 'template_pre_dirname' => 0x08,
+ 'header_pre_dirname' => 0x08,
+ 'inline_pre_dirname' => 0x08,
+ 'documentation_pre_dirname' => 0x08,
+ 'resource_pre_dirname' => 0x08,
+ 'generic_pre_dirname' => 0x08,
+ 'source_outputext' => 0x0a,
+ 'template_outputext' => 0x0a,
+ 'header_outputext' => 0x0a,
+ 'inline_outputext' => 0x0a,
+ 'documentation_outputext' => 0x0a,
+ 'resource_outputext' => 0x0a,
+ 'generic_outputext' => 0x0a,
+ );
+
+## Custom sections as well as definitions
+## Value Meaning
+## 0 No modifications
+## 1 Needs <%...%> conversion
+my %custom = ('command' => 1,
+ 'commandflags' => 1,
+ 'dependent' => 1,
+ 'gendir' => 0,
+ 'precommand' => 1,
+ 'postcommand' => 1,
+ );
+
+## All matching assignment arrays will get these keywords
+my @default_matching_assignments = ('recurse',
+ );
+
+## Deal with these components in a special way
+my %specialComponents = ('header_files' => 1,
+ 'inline_files' => 1,
+ 'template_files' => 1,
+ );
+my %sourceComponents = ('source_files' => 1,
+ 'template_files' => 1,
+ );
+
+my $defgroup = 'default_group';
+my $grouped_key = 'grouped_';
+my $tikey = '/ti/';
+
+## Matches with generic_outputext
+my $generic_key = 'generic_files';
+
+# ************************************************************
+# C++ Specific Component Settings
+# ************************************************************
+
+## Resource files tag for C++
+my $cppresource = 'resource_files';
+
+## Valid component names within a project along with the valid file extensions
+my %cppvc = ('source_files' => [ "\\.cpp", "\\.cxx", "\\.cc", "\\.c", "\\.C", ],
+ 'template_files' => [ "_T\\.cpp", "_T\\.cxx", "_T\\.cc", "_T\\.c", "_T\\.C", "_t\\.cpp", "_t\\.cxx", "_t\\.cc", "_t\\.c", "_t\\.C" ],
+ 'header_files' => [ "\\.h", "\\.hpp", "\\.hxx", "\\.hh", ],
+ 'inline_files' => [ "\\.i", "\\.ipp", "\\.inl", ],
+ 'documentation_files' => [ "README", "readme", "\\.doc", "\\.txt", "\\.html" ],
+ $cppresource => [ "\\.rc", ],
+ );
+
+## Exclude these extensions when auto generating the component values
+my %cppec = ('source_files' => $cppvc{'template_files'},
+ );
+
+## These matching assignment arrays will get added, but only to the
+## specific project component types.
+my %cppma = ('source_files' => ['buildflags',
+ 'managed',
+ 'no_pch',
+ ],
+ );
+
+# ************************************************************
+# C# Specific Component Settings
+# ************************************************************
+
+## Resource files tag for C#
+my $csresource = 'resx_files';
+
+## Valid component names within a project along with the valid file extensions
+my %csvc = ('source_files' => [ "\\.cs" ],
+ 'config_files' => [ "\\.config" ],
+ $csresource => [ "\\.resx", "\\.resources" ],
+ 'aspx_files' => [ "\\.aspx" ],
+ 'ico_files' => [ "\\.ico" ],
+ 'documentation_files' => [ "README", "readme", "\\.doc", "\\.txt", "\\.html" ],
+ );
+
+my %csma = ('source_files' => [ 'dependent_upon',
+ 'subtype',
+ ],
+ $csresource => [ 'dependent_upon',
+ 'generates_source',
+ 'subtype',
+ ],
+ );
+
+# ************************************************************
+# Java Specific Component Settings
+# ************************************************************
+
+## Valid component names within a project along with the valid file extensions
+my %jvc = ('source_files' => [ "\\.java" ],
+ 'documentation_files' => [ "README", "readme", "\\.doc", "\\.txt", "\\.html" ],
+ );
+
+# ************************************************************
+# Visual Basic Specific Component Settings
+# ************************************************************
+
+## Resource files tag for VB
+my $vbresource = 'resx_files';
+
+## Valid component names within a project along with the valid file extensions
+my %vbvc = ('source_files' => [ "\\.vb" ],
+ 'config_files' => [ "\\.config" ],
+ $vbresource => [ "\\.resx" ],
+ 'aspx_files' => [ "\\.aspx" ],
+ 'ico_files' => [ "\\.ico" ],
+ 'documentation_files' => [ "README", "readme", "\\.doc", "\\.txt", "\\.html" ],
+ );
+
+my %vbma = ('source_files' => [ 'subtype' ],
+ );
+
+# ************************************************************
+# Language Specific Component Settings
+# ************************************************************
+
+# Index Description
+# ----- -----------
+# 0 File types
+# 1 Files automatically excluded from source_files
+# 2 Assignments available in standard file types
+# 3 The entry point for executables
+# 4 The language uses a C preprocessor
+# 5 Name of the tag for 'resource_files' for this language
+# * This is special because it gets treated like source_files in that
+# a project with only these files is a library/exe not "custom only".
+my %language = (Creator::cplusplus => [ \%cppvc, \%cppec, \%cppma, 'main',
+ 1, $cppresource ],
+
+ Creator::csharp => [ \%csvc, {}, \%csma, 'Main', 0,
+ $csresource ],
+
+ Creator::java => [ \%jvc, {}, {}, 'main', 0 ],
+
+ Creator::vb => [ \%vbvc, {}, \%vbma, 'Main', 0,
+ $vbresource ],
+ );
+my %mains;
+
+# ************************************************************
+# Subroutine Section
+# ************************************************************
+
+sub new {
+ my($class, $global, $inc, $template, $ti, $dynamic, $static, $relative, $addtemp, $addproj, $progress, $toplevel, $baseprojs, $gfeature, $relative_f, $feature, $features, $hierarchy, $exclude, $makeco, $nmod, $applypj, $genins, $into, $language, $use_env, $expandvars, $gendot, $comments, $foreclipse) = @_;
+ my $self = $class->SUPER::new($global, $inc,
+ $template, $ti, $dynamic, $static,
+ $relative, $addtemp, $addproj,
+ $progress, $toplevel, $baseprojs,
+ $feature, $features,
+ $hierarchy, $nmod, $applypj,
+ $into, $language, $use_env,
+ $expandvars,
+ 'project');
+
+ $self->{$self->{'type_check'}} = 0;
+ $self->{'feature_defined'} = 0;
+ $self->{'features_changed'} = undef;
+ $self->{'project_info'} = [];
+ $self->{'lib_locations'} = {};
+ $self->{'reading_parent'} = [];
+ $self->{'dll_exe_template_input'}= {};
+ $self->{'lib_exe_template_input'}= {};
+ $self->{'lib_template_input'} = {};
+ $self->{'dll_template_input'} = {};
+ $self->{'flag_overrides'} = {};
+ $self->{'custom_special_output'} = {};
+ $self->{'custom_special_depend'} = {};
+ $self->{'special_supplied'} = {};
+ $self->{'pctype'} = $self->extractType("$self");
+ $self->{'verbatim'} = {};
+ $self->{'verbatim_accessed'} = {$self->{'pctype'} => {}};
+ $self->{'defaulted'} = {};
+ $self->{'custom_types'} = {};
+ $self->{'parents_read'} = {};
+ $self->{'inheritance_tree'} = {};
+ $self->{'remove_files'} = {};
+ $self->{'expanded'} = {};
+ $self->{'gfeature_file'} = $gfeature;
+ $self->{'relative_file'} = $relative_f;
+ $self->{'feature_parser'} = $self->create_feature_parser($features,
+ $feature);
+ $self->{'sort_files'} = $self->sort_files();
+ $self->{'source_callback'} = undef;
+ $self->{'dollar_special'} = $self->dollar_special();
+ $self->{'generate_ins'} = $genins;
+ $self->{'addtemp_state'} = undef;
+ $self->{'command_subs'} = $self->get_command_subs();
+ $self->{'escape_spaces'} = $self->escape_spaces();
+ $self->{'current_template'} = undef;
+ $self->{'make_coexistence'} = $makeco;
+
+ $self->add_default_matching_assignments();
+ $self->reset_generating_types();
+
+ return $self;
+}
+
+
+sub is_keyword {
+ ## Is the name passed in a known keyword for a project. This includes
+ ## keywords mapped by Define_Custom or Modify_Custom.
+ my($self, $name) = @_;
+ return $self->{'valid_names'}->{$name};
+}
+
+
+sub read_global_configuration {
+ my $self = shift;
+ my $input = $self->get_global_cfg();
+ my $status = 1;
+
+ if (defined $input) {
+ ## If it doesn't contain a path, search the include path
+ if ($input !~ /[\/\\]/) {
+ $input = $self->search_include_path($input);
+ $input = $self->get_global_cfg() if (!defined $input);
+ }
+
+ ## Read and parse the global project file
+ $self->{'reading_global'} = 1;
+ $status = $self->parse_file($input);
+ $self->{'reading_global'} = 0;
+ }
+
+ return $status;
+}
+
+
+sub convert_to_template_assignment {
+ my($self, $name, $value, $calledfrom) = @_;
+
+ ## If the value we are going to set for $name has been used as a
+ ## scoped template variable, we need to hijack the whole assignment
+ ## and turn it into a template variable assignment.
+ my $atemp = $self->get_addtemp();
+ foreach my $key (grep(/::$name$/, keys %$atemp)) {
+ $self->update_template_variable(0, $calledfrom, $key, $value);
+ }
+}
+
+
+sub create_recursive_settings {
+ my($self, $name, $value, $assign) = @_;
+
+ ## Handle both recursive_includes and recursive_libpaths in one
+ ## search and replace.
+ if ($name =~ s/^recursive_//) {
+ ## This portion of code was lifted directly from Creator::relative()
+ ## but modified to always expand the variables. We will turn the
+ ## expanded values back into variables below and once they're passed
+ ## off to the assignment processing code, they will be turned into
+ ## relative values (if possible).
+ if (index($value, '$') >= 0) {
+ my $ovalue = $value;
+ my($rel, $how) = $self->get_initial_relative_values();
+ $value = $self->expand_variables($value, $rel, 0, undef, 1);
+
+ if ($ovalue eq $value || index($value, '$') >= 0) {
+ ($rel, $how) = $self->get_secondary_relative_values();
+ $value = $self->expand_variables($value, $rel, 0, undef, 1, 1);
+ }
+ }
+
+ ## Create an array out of the recursive directory list. Convert all
+ ## of the relative or full path values back into $() values.
+ my @dirs = ();
+ my $elems = $self->create_array($value);
+ foreach my $elem (@$elems) {
+ my $dlist = $self->recursive_directory_list($elem, []);
+ if ($dlist eq '') {
+ ## This directory doesn't exist, just add the original value
+ push(@dirs, $elem);
+ }
+ else {
+ ## Create an array out of the directory list and add it to our
+ ## array.
+ my $array = $self->create_array($dlist);
+ push(@dirs, @$array);
+ }
+ }
+
+ ## We need to return a string, so we join it all together space
+ ## separated.
+ $value = join(' ', $self->back_to_variable(\@dirs));
+ }
+
+ return $name, $value;
+}
+
+sub process_assignment {
+ my($self, $name, $value, $assign, $calledfrom) = @_;
+ $calledfrom = 0 if (!defined $calledfrom);
+
+ ## See if the name is one of the special "recursive" settings. If so,
+ ## fix up the value and change the name.
+ ($name, $value) = $self->create_recursive_settings($name, $value, $assign);
+
+ ## Support the '*' mechanism as in the project name, to allow
+ ## the user to correctly depend on another project within the same
+ ## directory.
+ if (defined $value) {
+ if ($name eq 'after' && index($value, '*') >= 0) {
+ $value = $self->fill_type_name($value,
+ $self->get_default_project_name());
+ }
+
+ ## If this particular project type does not consider the dollar sign
+ ## special and the user has provided two dollarsigns as an escape, we
+ ## will turn it into a single dollar sign.
+ if (!$self->{'dollar_special'} && index($value, '$$') >= 0) {
+ $value =~ s/\$\$/\$/g;
+ }
+
+ ## If the assignment name is valid and requires parameter (<%...%>)
+ ## replacement, then do so.
+ if (defined $self->{'valid_names'}->{$name} &&
+ ($self->{'valid_names'}->{$name} & 0x04) == 0 &&
+ index($value, '<%') >= 0) {
+ $value = $self->replace_parameters($value, $self->{'command_subs'});
+ }
+ }
+
+ if ($calledfrom == 0) {
+ $self->convert_to_template_assignment($name, $value, $calledfrom);
+ }
+
+ ## Call the base process_assigment() after we have modified the name and
+ ## value.
+ $self->SUPER::process_assignment($name, $value, $assign);
+
+ ## Support keyword mapping here only at the project level scope. The
+ ## scoped keyword mapping is done through the parse_scoped_assignment()
+ ## method.
+ if (!defined $assign || $assign == $self->get_assignment_hash()) {
+ my $mapped = $self->{'valid_names'}->{$name};
+ if (defined $mapped && UNIVERSAL::isa($mapped, 'ARRAY')) {
+ $self->parse_scoped_assignment($$mapped[0], 0,
+ $$mapped[1], $value,
+ $self->{'generated_exts'}->{$$mapped[0]});
+ }
+ }
+}
+
+
+sub process_assignment_add {
+ my($self, $name, $value, $assign) = @_;
+
+ ## See if the name is one of the special "recursive" settings. If so,
+ ## fix up the value and change the name.
+ ($name, $value) = $self->create_recursive_settings($name, $value, $assign);
+
+ return $self->SUPER::process_assignment_add($name, $value, $assign);
+}
+
+
+sub process_assignment_sub {
+ my($self, $name, $value, $assign) = @_;
+
+ ## See if the name is one of the special "recursive" settings. If so,
+ ## fix up the value and change the name.
+ ($name, $value) = $self->create_recursive_settings($name, $value, $assign);
+
+ return $self->SUPER::process_assignment_sub($name, $value, $assign);
+}
+
+
+sub addition_core {
+ my($self, $name, $value, $nval, $assign) = @_;
+
+ ## If there is a previous value ($nval) and the keyword is going to be
+ ## evaled, we need to separate the values with a command separator.
+ ## This has to be done at the MPC level because it isn't always
+ ## possible for the user to know if a value has already been added to
+ ## the keyword (prebuild, postbuild and postclean).
+ if (defined $nval &&
+ defined $validNames{$name} && ($validNames{$name} & 4)) {
+ if ($self->preserve_assignment_order($name)) {
+ $value = '<%cmdsep%> ' . $value;
+ }
+ else {
+ $value .= '<%cmdsep%>';
+ }
+ }
+
+ ## For an addition, we need to see if it is a project keyword being
+ ## used within a 'specific' section. If it is, we may need to update
+ ## scoped settings for that variable (which are in essence template
+ ## variables).
+ $self->convert_to_template_assignment($name, $value, 1);
+
+ ## Next, we just give everything to the base class method.
+ $self->SUPER::addition_core($name, $value, $nval, $assign);
+}
+
+
+sub subtraction_core {
+ my($self, $name, $value, $nval, $assign) = @_;
+
+ ## For a subtraction, we need to see if it is a project keyword being
+ ## used within a 'specific' section. If it is, we may need to update
+ ## scoped settings for that variable (which are in essence template
+ ## variables).
+ $self->convert_to_template_assignment($name, $value, -1);
+
+ ## Next, we just give everything to the base class method.
+ $self->SUPER::subtraction_core($name, $value, $nval, $assign);
+}
+
+
+sub get_assignment_for_modification {
+ my($self, $name, $assign, $subtraction) = @_;
+
+ ## If we weren't passed an assignment hash, then we need to
+ ## look one up that may possibly correctly deal with keyword mappings
+ if (!defined $assign) {
+ my $mapped = $self->{'valid_names'}->{$name};
+
+ if (defined $mapped && UNIVERSAL::isa($mapped, 'ARRAY')) {
+ $name = $$mapped[1];
+ $assign = $self->{'generated_exts'}->{$$mapped[0]};
+ }
+ }
+
+ ## Get the assignment value
+ my $value = $self->get_assignment($name, $assign);
+
+ ## If we are involved in a subtraction, we get back a value and
+ ## it's a scoped or mapped assignment, then we need to possibly
+ ## expand any template variables. Otherwise, the subtractions
+ ## may not work correctly.
+ if ($subtraction && defined $value && defined $assign) {
+ $value = $self->relative($value, 1);
+ }
+
+ return $value;
+}
+
+
+sub begin_project {
+ my($self, $parents) = @_;
+ my $status = 1;
+ my $error;
+
+ ## Deal with the inheritance hierarchy first
+ ## Add in the base projects from the command line
+ if (!$self->{'reading_global'} &&
+ !defined $self->{'reading_parent'}->[0]) {
+ my $baseprojs = $self->get_baseprojs();
+
+ if (defined $parents) {
+ foreach my $base (@$baseprojs) {
+ push(@$parents, $base) if (!StringProcessor::fgrep($base, $parents));
+ }
+ }
+ else {
+ $parents = $baseprojs;
+ }
+ }
+
+ if (defined $parents) {
+ foreach my $parent (@$parents) {
+ ## Read in the parent onto ourself
+ my $file = $self->search_include_path(
+ "$parent.$BaseClassExtension");
+ if (!defined $file) {
+ $file = $self->search_include_path(
+ "$parent.$ProjectCreatorExtension");
+ }
+
+ if (defined $file) {
+ if (defined $self->{'reading_parent'}->[0]) {
+ if (StringProcessor::fgrep($file, $self->{'reading_parent'})) {
+ $status = 0;
+ $error = 'Cyclic inheritance detected: ' . $parent;
+ }
+ }
+
+ if ($status) {
+ if (!defined $self->{'parents_read'}->{$file}) {
+ $self->{'parents_read'}->{$file} = 1;
+
+ ## Push the base project file onto the parent stack
+ push(@{$self->{'reading_parent'}}, $file);
+
+ ## Collect up some information about the inheritance tree
+ my $tree = $self->{'current_input'};
+ if (!defined $self->{'inheritance_tree'}->{$tree}) {
+ $self->{'inheritance_tree'}->{$tree} = {};
+ }
+ my $hash = $self->{'inheritance_tree'}->{$tree};
+ foreach my $p (@{$self->{'reading_parent'}}) {
+ $$hash{$p} = {} if (!defined $$hash{$p});
+ $hash = $$hash{$p};
+ }
+
+ ## Begin reading the parent
+ $status = $self->parse_file($file);
+
+ ## Take the base project file off of the parent stack
+ pop(@{$self->{'reading_parent'}});
+
+ $error = "Invalid parent: $parent" if (!$status);
+ }
+ else {
+ ## The base project has already been read. So, if
+ ## we are reading the original project (not a parent base
+ ## project), then the current base project is redundant.
+ if (!defined $self->{'reading_parent'}->[0]) {
+ $file =~ s/\.[^\.]+$//;
+ $self->information('Inheriting from \'' .
+ $self->mpc_basename($file) .
+ '\' in ' . $self->{'current_input'} .
+ ' is redundant at line ' .
+ $self->get_line_number() . '.');
+ }
+ }
+ }
+ }
+ else {
+ $status = 0;
+ $error = "Unable to locate parent: $parent";
+ }
+ }
+ }
+
+ ## Copy each value from global_assign into assign
+ if (!$self->{'reading_global'}) {
+ foreach my $key (keys %{$self->{'global_assign'}}) {
+ if (!defined $self->{'assign'}->{$key}) {
+ $self->{'assign'}->{$key} = $self->{'global_assign'}->{$key};
+ }
+ }
+ }
+
+ return $status, $error;
+}
+
+
+sub get_process_project_type {
+ my($self, $types) = @_;
+ my $type = '';
+ my $defcomp = $self->get_default_component_name();
+
+ foreach my $t (split(/\s*,\s*/, $types)) {
+ my $not = ($t =~ s/^!\s*//);
+ if ($not) {
+ if ($t eq $self->{'pctype'}) {
+ $type = '';
+ last;
+ }
+ else {
+ $type = $self->{'pctype'};
+ }
+ }
+ elsif ($t eq $self->{'pctype'} || $t eq $defcomp) {
+ $type = $t;
+ last;
+ }
+ }
+
+ return $type;
+}
+
+
+sub matches_specific_scope {
+ my($self, $elements) = @_;
+
+ ## First check for properties that correspond to the current project
+ ## type. Elements that begin with "prop:" indicate a property.
+ my $list = '';
+ my $props = $self->get_properties();
+ foreach my $prop (split(/\s*,\s*/, $elements)) {
+ my $not = ($prop =~ s/^!\s*//);
+ if ($prop =~/(.+):(.+)/) {
+ if ($1 eq 'prop') {
+ $prop = $2;
+ if ($not) {
+ return $self->{'pctype'} if (!$$props{$prop});
+ }
+ else {
+ return $self->{'pctype'} if ($$props{$prop});
+ }
+ }
+ else {
+ $self->warning("$prop is not recognized.");
+ }
+ }
+ else {
+ $list .= ($not ? '!' : '') . $prop . ',';
+ }
+ }
+
+ ## If none of the elements match a property, then check the type
+ ## against the current project type or the default component name
+ ## (which is what it would be set to if a specific clause is used with
+ ## out parenthesis).
+ my $type = $self->get_process_project_type($list);
+ return $self->{'pctype'} if ($type eq $self->{'pctype'} ||
+ $type eq $self->get_default_component_name());
+
+ ## Nothing matched
+ return undef;
+}
+
+
+sub parse_line {
+ my($self, $ih, $line) = @_;
+ my($status,
+ $errorString,
+ @values) = $self->parse_known($line);
+
+ ## parse_known() passes back an array of values
+ ## that make up the contents of the line parsed.
+ ## The array can have 0 to 3 items. The first,
+ ## if defined, is always an identifier of some
+ ## sort.
+
+ if ($status && defined $values[0]) {
+ if ($values[0] eq $self->{'grammar_type'}) {
+ my $name = $values[1];
+ my $typecheck = $self->{'type_check'};
+ if (defined $name && $name eq '}') {
+ ## Project Ending
+ if (!defined $self->{'reading_parent'}->[0] &&
+ !$self->{'reading_global'}) {
+ ## Fill in all the default values
+ $self->generate_defaults();
+
+ ## Perform any additions, subtractions
+ ## or overrides for the project values.
+ my $addproj = $self->get_addproj();
+ foreach my $ap (keys %$addproj) {
+ if (defined $self->{'valid_names'}->{$ap}) {
+ foreach my $val (@{$$addproj{$ap}}) {
+ if ($$val[0] > 0) {
+ $self->process_assignment_add($ap, $$val[1]);
+ }
+ elsif ($$val[0] < 0) {
+ $self->process_assignment_sub($ap, $$val[1]);
+ }
+ else {
+ $self->process_assignment($ap, $$val[1]);
+ }
+ }
+ }
+ else {
+ $errorString = 'Invalid ' .
+ "assignment modification name: $ap";
+ $status = 0;
+ }
+ }
+
+ if ($status) {
+ ## Generate default target names after all source files are added
+ ## and after we've added in all of the options from the
+ ## command line. If the user set exename on the command line
+ ## and no "main" is found, sharedname will be set too and
+ ## most templates do not handle that well.
+ $self->generate_default_target_names();
+
+ ## End of project; Write out the file.
+ ($status, $errorString) = $self->write_project();
+
+ ## write_project() can return 0 for error, 1 for project
+ ## was written and 2 for project was skipped
+ if ($status == 1) {
+ ## Save the library name and location
+ foreach my $name ('sharedname', 'staticname') {
+ my $val = $self->get_assignment($name);
+ if (defined $val) {
+ my $cwd = $self->getcwd();
+ my $start = $self->getstartdir();
+ my $amount = 0;
+ if ($cwd eq $start) {
+ $amount = length($start);
+ }
+ elsif (index($cwd, $start) == 0) {
+ $amount = length($start) + 1;
+ }
+ $self->{'lib_locations'}->{$val} =
+ substr($cwd, $amount);
+ last;
+ }
+ }
+
+ ## Check for unused verbatim markers
+ foreach my $key (keys %{$self->{'verbatim'}}) {
+ if (defined $self->{'verbatim_accessed'}->{$key}) {
+ foreach my $ikey (keys %{$self->{'verbatim'}->{$key}}) {
+ if (!defined $self->{'verbatim_accessed'}->{$key}->{$ikey}) {
+ $self->warning("Marker $ikey does not exist.");
+ }
+ }
+ }
+ }
+ }
+
+ ## Reset all of the project specific data
+ foreach my $key (keys %{$self->{'valid_components'}}) {
+ delete $self->{$key};
+ $self->{'defaulted'}->{$key} = 0;
+ }
+ if (defined $self->{'addtemp_state'}) {
+ $self->restore_state($self->{'addtemp_state'}, 'addtemp');
+ $self->{'addtemp_state'} = undef;
+ }
+ $self->{'assign'} = {};
+ $self->{'verbatim'} = {};
+ $self->{'verbatim_accessed'} = {$self->{'pctype'} => {}};
+ $self->{'special_supplied'} = {};
+ $self->{'flag_overrides'} = {};
+ $self->{'parents_read'} = {};
+ $self->{'inheritance_tree'} = {};
+ $self->{'remove_files'} = {};
+ $self->{'custom_special_output'} = {};
+ $self->{'custom_special_depend'} = {};
+ $self->{'expanded'} = {};
+ $self->reset_generating_types();
+ }
+ }
+ $self->{$typecheck} = 0;
+ }
+ else {
+ ## Project Beginning
+ ($status, $errorString) = $self->begin_project($values[2]);
+
+ ## Set up the default project name
+ if ($status) {
+ if (defined $name) {
+ if ($name =~ /[\/\\]/) {
+ $status = 0;
+ $errorString = 'Projects can not have a slash ' .
+ 'or a back slash in the name';
+ }
+ else {
+ ## We should only set the project name if we are not
+ ## reading in a parent project.
+ if (!defined $self->{'reading_parent'}->[0]) {
+ $name =~ s/^\(\s*//;
+ $name =~ s/\s*\)$//;
+ $name = $self->transform_file_name($name);
+
+ ## Replace any *'s with the default name
+ if (index($name, '*') >= 0) {
+ $name = $self->fill_type_name(
+ $name,
+ $self->get_default_project_name());
+ }
+
+ $self->set_project_name($name);
+ }
+ else {
+ $self->warning("Ignoring project name " .
+ "$name in a base project.");
+ }
+ }
+ }
+ }
+
+ ## Signify that we have a valid project
+ $self->{$typecheck} = 1 if ($status);
+ }
+ }
+ elsif ($values[0] eq '0') {
+ ## $values[1] = name; $values[2] = value
+ if (defined $self->{'valid_names'}->{$values[1]}) {
+ $self->process_assignment($values[1], $values[2]);
+ }
+ else {
+ $errorString = "Invalid assignment name: '$values[1]'";
+ $status = 0;
+ }
+ }
+ elsif ($values[0] eq '1') {
+ ## $values[1] = name; $values[2] = value
+ if (defined $self->{'valid_names'}->{$values[1]}) {
+ $self->process_assignment_add($values[1], $values[2]);
+ }
+ else {
+ $errorString = "Invalid addition name: $values[1]";
+ $status = 0;
+ }
+ }
+ elsif ($values[0] eq '-1') {
+ ## $values[1] = name; $values[2] = value
+ if (defined $self->{'valid_names'}->{$values[1]}) {
+ $self->process_assignment_sub($values[1], $values[2]);
+ }
+ else {
+ $errorString = "Invalid subtraction name: $values[1]";
+ $status = 0;
+ }
+ }
+ elsif ($values[0] eq 'component') {
+ my $comp = $values[1];
+ my $name = $values[2];
+ my $vc = $self->{'valid_components'};
+
+ if (defined $$vc{$comp}) {
+ ($status, $errorString) = $self->parse_components($ih, $comp, $name);
+ }
+ else {
+ if ($comp eq 'verbatim') {
+ my($type, $loc, $add) = split(/\s*,\s*/, $name);
+ ($status, $errorString) = $self->parse_verbatim($ih, $type,
+ $loc, $add);
+ }
+ elsif ($comp eq 'specific') {
+ my $type = $self->matches_specific_scope($name);
+ if (defined $type) {
+ ($status, $errorString) = $self->parse_scope(
+ $ih, $comp, $type,
+ $self->{'valid_names'},
+ $self->get_assignment_hash(),
+ {});
+ }
+ else {
+ ## We still need to parse the scope, but we will be
+ ## throwing away whatever is processed. However, it
+ ## could still be invalid code that will cause an error.
+ ($status, $errorString) = $self->parse_scope(
+ $ih, $comp, undef,
+ $self->{'valid_names'},
+ undef,
+ $self->get_assignment_hash());
+ }
+ }
+ elsif ($comp eq 'define_custom') {
+ ($status, $errorString) = $self->parse_define_custom($ih, $name);
+ }
+ elsif ($comp eq 'modify_custom') {
+ ($status, $errorString) = $self->parse_define_custom($ih, $name, 1);
+ }
+ elsif ($comp eq 'expand') {
+ $self->{'parsing_expand'} = 1;
+ ($status, $errorString) = $self->parse_scope($ih, $comp, $name);
+ $self->{'parsing_expand'} = undef;
+ }
+ else {
+ $errorString = "Invalid component name: $comp";
+ $status = 0;
+ }
+ }
+ }
+ elsif ($values[0] eq 'feature') {
+ $self->{'feature_defined'} = 1;
+ ($status, $errorString) = $self->process_feature($ih,
+ $values[1],
+ $values[2]);
+ if ($status && $self->{'feature_defined'}) {
+ $errorString = "Did not find the end of the feature";
+ $status = 0;
+ }
+ }
+ else {
+ $errorString = "Unrecognized line: $line";
+ $status = 0;
+ }
+ }
+ elsif ($status == -1) {
+ $status = 0;
+ }
+
+ return $status, $errorString;
+}
+
+
+sub parse_scoped_assignment {
+ my($self, $tag, $type, $name, $value, $flags) = @_;
+
+ ## Map the assignment name on a scoped assignment
+ my $mapped = $self->{'valid_names'}->{$name};
+ if (defined $mapped && UNIVERSAL::isa($mapped, 'ARRAY')) {
+ $name = $$mapped[1];
+ }
+
+ if (defined $self->{'matching_assignments'}->{$tag} &&
+ StringProcessor::fgrep($name, $self->{'matching_assignments'}->{$tag})) {
+ my $over = {};
+ if (defined $self->{'flag_overrides'}->{$tag}) {
+ $over = $self->{'flag_overrides'}->{$tag};
+ }
+ else {
+ $self->{'flag_overrides'}->{$tag} = $over;
+ }
+
+ if ($type == 0) {
+ $self->process_assignment($name, $value, $flags);
+ }
+ elsif ($type == 1) {
+ ## If there is no value in $$flags, then we need to get
+ ## the outer scope value and put it in there.
+ if (!defined $self->get_assignment($name, $flags)) {
+ my $outer = $self->get_assignment($name);
+ $self->process_assignment($name, $outer, $flags);
+ }
+ $self->process_assignment_add($name, $value, $flags);
+ }
+ elsif ($type == -1) {
+ ## If there is no value in $$flags, then we need to get
+ ## the outer scope value and put it in there.
+ if (!defined $self->get_assignment($name, $flags)) {
+ my $outer = $self->get_assignment($name);
+ $self->process_assignment($name, $outer, $flags);
+ }
+ $self->process_assignment_sub($name, $value, $flags);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+
+sub update_template_variable {
+ my $self = shift;
+ my $check = shift;
+ my @values = @_;
+
+ ## Save the addtemp state if we haven't done so before
+ if (!defined $self->{'addtemp_state'}) {
+ my %state = $self->save_state('addtemp');
+ $self->{'addtemp_state'} = \%state;
+ }
+
+ ## If the name that is used within a specific is a mapped keyword
+ ## then we need to translate it into the mapped keyword as it will
+ ## be used by the TemplateParser.
+ my $name;
+ if ($values[1] =~ /(.*::)(.*)/) {
+ my $base = $1;
+ my $mapped = $self->{'valid_names'}->{$2};
+ if (defined $mapped && UNIVERSAL::isa($mapped, 'ARRAY')) {
+ $name = $values[1];
+ $values[1] = $base . 'custom_type->' . $$mapped[1];
+ }
+ }
+
+ ## Now modify the addtemp values
+ my $atemp = $self->get_addtemp();
+ $self->information("'$values[1]' was used as a template modifier.");
+
+ if ($check && !defined $atemp->{$values[1]}) {
+ $name = $values[1] if (!defined $name);
+ if ($name =~ s/.*:://) {
+ my $value = $self->get_assignment($name);
+ ## Regardless of whether there was and assignment value, we need to
+ ## look at the template value of the base so that modification of a
+ ## scoped variable includes the base values.
+ if (defined $atemp->{$name}) {
+ foreach my $arr (@{$atemp->{$name}}) {
+ my @copy = @$arr;
+ push(@{$atemp->{$values[1]}}, \@copy);
+ }
+ }
+ unshift(@{$atemp->{$values[1]}},
+ [0, $value, undef, $name]) if (defined $value);
+ }
+ }
+
+ ## Subsitute all pseudo variables for the project specific characters.
+ $values[2] = $self->replace_parameters($values[2], $self->{'command_subs'})
+ if (index($values[2], '<%') >= 0);
+
+ if (defined $atemp->{$values[1]}) {
+ ## If there are template variable settings, then we need to add
+ ## this new one to the end of the settings that did not come from
+ ## the command line. That way, adjust_value() does not need to
+ ## sort the values (and have knowledge about which came from the
+ ## command line and which didn't).
+ my $max = scalar(@{$atemp->{$values[1]}});
+ for(my $i = 0; $i < $max; $i++) {
+ if ($atemp->{$values[1]}->[$i]->[2]) {
+ splice(@{$atemp->{$values[1]}}, $i, 0,
+ [$values[0], $values[2], undef, $name]);
+ return;
+ }
+ }
+ }
+ else {
+ $atemp->{$values[1]} = [];
+ }
+
+ ## If the variable name is not scoped, we need to look through existing
+ ## scoped variables that match the base. If we find one, we need to
+ ## propagate this value into the scoped settings.
+ if (index($values[1], '::') == -1) {
+ $name = $values[1] if (!defined $name);
+ foreach my $key (keys %$atemp) {
+ if ($key ne $name) {
+ foreach my $entry (@{$atemp->{$key}}) {
+ if ($$entry[3] eq $name) {
+ push(@{$atemp->{$key}}, [$values[0], $values[2], undef, $name]);
+ last;
+ }
+ }
+ }
+ }
+ }
+
+ ## 0: (0 set, 1 add, -1 subtract)
+ ## 1: The text value
+ ## 2: (true set on command line, false set in project)
+ ## 3: The original variable name if it's scoped or mapped
+ push(@{$atemp->{$values[1]}}, [$values[0], $values[2], undef, $name]);
+}
+
+
+sub handle_unknown_assignment {
+ my $self = shift;
+ my $type = shift;
+ my @values = @_;
+
+ ## Unknown assignments within a 'specific' section are handled as
+ ## template value modifications. These are handled exactly as the
+ ## -value_template option in Options.pm.
+
+ ## If $type is not defined, then we are skipping this section
+ $self->update_template_variable(1, @values) if (defined $type);
+
+ return 1, undef;
+}
+
+
+sub handle_scoped_unknown {
+ my($self, $fh, $type, $flags, $line) = @_;
+
+ if (defined $type && $self->{'parsing_expand'}) {
+ if ($type eq $self->get_default_component_name()) {
+ return 0, 'Can not set expansion in this context';
+ }
+ else {
+ if (!defined $self->{'expanded'}->{$type}) {
+ my $undef = $self->replace_env_vars(\$line);
+ if (!$undef) {
+ ## This is a special concession for Windows. It will not allow
+ ## you to set an empty environment variable. If an empty
+ ## double quoted string is found, we will assume that the user
+ ## wanted an empty string.
+ $line = '' if ($line eq '""');
+
+ $self->{'expanded'}->{$type} = $line;
+ }
+ }
+ return 1, undef;
+ }
+ }
+
+ ## If the type is not defined, then this is something other than an
+ ## assignment in a 'specific' section and should be flagged as an error
+ return 0, "Unrecognized line: $line";
+}
+
+sub process_component_line {
+ my($self, $tag, $line, $flags,
+ $grname, $current, $excarr, $comps, $count) = @_;
+ my $status = 1;
+ my $error;
+ my %exclude;
+ my @values;
+
+ ## If this returns true, then we've found an assignment
+ if ($self->parse_assignment($line, \@values)) {
+ $status = $self->parse_scoped_assignment($tag, @values, $flags);
+ if (!$status) {
+ $error = 'Unknown keyword: ' . $values[1];
+ }
+ }
+ else {
+ ## If we successfully remove a '!' from the front, then
+ ## the file(s) listed are to be excluded
+ my $rem = ($line =~ s/^\^\s*//);
+ my $exc = $rem || ($line =~ s/^!\s*//);
+
+ ## Convert any $(...) in this line before we process any
+ ## wild card characters. If we do not, scoped assignments will
+ ## not work nor will we get the correct wild carded file list.
+ ## We also need to make sure that any back slashes are converted to
+ ## slashes to ensure that later flag_overrides checks will happen
+ ## correctly.
+ $line = $self->relative($line);
+ $line =~ s/\\/\//g if ($self->{'convert_slashes'});
+
+ ## Now look for specially listed files.
+ ## Regular expressions are very slow. Searching the line twice with
+ ## index() is 328 times faster than searching with just the regular
+ ## expression when it doesn't match (which is likely to be the case).
+ if ((index($line, '>>') >= 0 || index($line, '<<') >= 0) &&
+ $line =~ /(.*)\s+(>>|<<)\s+(.*)/) {
+ $line = $1;
+ my $oop = $2;
+ my $iop = ($oop eq '>>' ? '<<' : '>>');
+ my $out = ($oop eq '>>' ? $3 : undef);
+ my $dep = ($oop eq '<<' ? $3 : undef);
+
+ $line =~ s/\s+$//;
+ if (index($line, $iop) >= 0 && $line =~ /(.*)\s+$iop\s+(.*)/) {
+ $line = $1;
+ $out = $2 if ($iop eq '>>');
+ $dep = $2 if ($iop eq '<<');
+ $line =~ s/\s+$//;
+ }
+
+ ## Check for both possible error conditions
+ if (index($line, $oop) >= 0) {
+ $status = 0;
+ $error = "Duplicate $oop used";
+ }
+ elsif (index($line, $iop) >= 0) {
+ $status = 0;
+ $error = "Duplicate $iop used";
+ }
+
+ ## Keys used internally to MPC need to be in forward slash format.
+ my $key = $line;
+ $key =~ s/\\/\//g if ($self->{'convert_slashes'});
+ if (defined $out) {
+ if (!defined $self->{'custom_special_output'}->{$tag}) {
+ $self->{'custom_special_output'}->{$tag} = {};
+ }
+ ## We can not convert slashes here as we do for dependencies
+ ## (below). The files specified here need to retain the forward
+ ## slashes as they are used elsewhere.
+ $self->{'custom_special_output'}->{$tag}->{$key} = $self->create_array($out);
+ }
+ if (defined $dep) {
+ $self->{'custom_special_depend'}->{$key} = $self->create_array($dep);
+ if ($self->{'convert_slashes'}) {
+ foreach my $depfile (@{$self->{'custom_special_depend'}->{$key}}) {
+ $depfile =~ s/\//\\/g;
+ }
+ }
+ }
+ }
+
+ ## If there is a command helper, we need to add the output files
+ ## here. It is possible that helper determined output files are
+ ## the only files added by this component type.
+ my $cmdHelper = CommandHelper::get($tag);
+ if (defined $cmdHelper) {
+ my $key = $line;
+ $key =~ s/\\/\//g if ($self->{'convert_slashes'});
+ my $cmdflags = $$flags{'commandflags'};
+ my $add_out = $cmdHelper->get_output($key, $cmdflags);
+ push(@{$self->{'custom_special_output'}->{$tag}->{$key}}, @$add_out);
+ }
+
+ ## Set up the files array. If the line contains a wild card
+ ## character use CORE::glob() to get the files specified.
+ my @files;
+ if ($line =~ /^"([^"]+)"$/) {
+ push(@files, $1);
+ }
+ ## Don't glob the line if we're wanting to remove the file. Wait
+ ## until later to do the wildcard expansion (in remove_excluded).
+ elsif (!$rem && $line =~ /[\?\*\[\]]/) {
+ @files = $self->mpc_glob($line);
+ }
+ else {
+ push(@files, $line);
+ }
+
+ ## If we want to remove these files at the end too, then
+ ## add them to our remove_files hash array.
+ if ($rem) {
+ if (!defined $self->{'remove_files'}->{$tag}) {
+ $self->{'remove_files'}->{$tag} = {};
+ }
+ foreach my $file (@files) {
+ $self->{'remove_files'}->{$tag}->{$file} = 1;
+ }
+ }
+
+ ## If we're excluding these files, then put them in the hash
+ if ($exc) {
+ $$grname = $current;
+ @exclude{@files} = (@files);
+ push(@$excarr, @files);
+ }
+ else {
+ ## Set the flag overrides for each file
+ my $over = $self->{'flag_overrides'}->{$tag};
+ if (defined $over) {
+ foreach my $file (@files) {
+ $$over{$file} = $flags;
+ }
+ }
+
+ foreach my $file (@files) {
+ ## Add the file if we're not excluding it
+ push(@{$$comps{$current}}, $file) if (!defined $exclude{$file});
+
+ ## The user listed a file explicitly, whether we
+ ## excluded it or not.
+ ++$$count;
+ }
+ }
+ }
+
+ return $status, $error;
+}
+
+
+sub parse_conditional {
+ my($self, $fh, $types, $tag, $flags,
+ $grname, $current, $exclude, $comps, $count) = @_;
+ my $status = 1;
+ my $error;
+ my $type = $self->matches_specific_scope($types);
+ my $add = (defined $type ? 1 : 0);
+
+ while(<$fh>) {
+ my $line = $self->preprocess_line($fh, $_);
+
+ if ($line eq '') {
+ }
+ elsif ($line =~ /^}\s*else\s*{$/) {
+ $add ^= 1;
+ }
+ elsif ($line =~ /^}$/) {
+ last;
+ }
+ elsif ($add) {
+ ($status, $error) = $self->process_component_line(
+ $tag, $line, $flags,
+ $grname, $current,
+ $exclude, $comps, $count);
+ last if (!$status);
+ }
+ }
+
+ return $status, $error;
+}
+
+sub parse_components {
+ my($self, $fh, $tag, $name) = @_;
+ my $current = $defgroup;
+ my $status = 1;
+ my $error;
+ my $names = {};
+ my $comps = {};
+ my $set;
+ my %flags;
+ my @exclude;
+ my $custom = defined $self->{'generated_exts'}->{$tag};
+ my $grtag = $grouped_key . $tag;
+ my $grname;
+
+ if ($custom) {
+ ## For the custom scoped assignments, we want to put a copy of
+ ## the original custom defined values in our flags associative array.
+ foreach my $key (keys %custom) {
+ if (defined $self->{'generated_exts'}->{$tag}->{$key}) {
+ $flags{$key} = $self->{'generated_exts'}->{$tag}->{$key};
+ }
+ }
+ }
+
+ if (defined $self->{$tag}) {
+ $names = $self->{$tag};
+ }
+ else {
+ $self->{$tag} = $names;
+ }
+ if (defined $$names{$name}) {
+ $comps = $$names{$name};
+ }
+ else {
+ $$names{$name} = $comps;
+ }
+ $$comps{$current} = [] if (!defined $$comps{$current});
+
+ my $count = 0;
+ while(<$fh>) {
+ my $line = $self->preprocess_line($fh, $_);
+
+ if ($line eq '') {
+ }
+ elsif ($line =~ /^(\w+)\s*{$/) {
+ if (!$set) {
+ $current = $1;
+ $set = 1;
+ $$comps{$current} = [] if (!defined $$comps{$current});
+ }
+ else {
+ $status = 0;
+ $error = 'Can not nest groups';
+ last;
+ }
+ }
+ elsif ($line =~ /^conditional\s*(\(([^\)]+)\))\s*{$/) {
+ ($status, $error) = $self->parse_conditional(
+ $fh, $2, $tag, \%flags, \$grname,
+ $current, \@exclude, $comps,
+ \$count);
+ last if (!$status);
+ }
+ elsif ($line =~ /^}$/) {
+ if (!defined $$comps{$current}->[0] && !defined $exclude[0]) {
+ ## The default components name was never used
+ ## so we remove it from the components
+ delete $$comps{$current};
+ }
+ else {
+ ## It was used, so we need to add that name to
+ ## the set of group names unless it's already been added.
+ $self->process_assignment_add($grtag, $current);
+ }
+ if ($set) {
+ $current = $defgroup;
+ $set = undef;
+ }
+ else {
+ ## We are at the end of a component. If the only group
+ ## we added was the default group, then we need to remove
+ ## the group setting altogether.
+ my $groups = $self->get_assignment($grtag);
+ if (defined $groups) {
+ my $grarray = $self->create_array($groups);
+ if (scalar(@$grarray) == 1 && $$grarray[0] eq $defgroup) {
+ $self->process_assignment($grtag, undef);
+ }
+ }
+
+ ## This is not an error,
+ ## this is the end of the components
+ last;
+ }
+ }
+ else {
+ ($status, $error) = $self->process_component_line($tag, $line, \%flags,
+ \$grname, $current,
+ \@exclude, $comps,
+ \$count);
+ last if (!$status);
+ }
+ }
+
+ ## If this is a "special" component, we need to see if the
+ ## user provided all directories. If they have, then we need to
+ ## store an array of directories that the user supplied. Otherwise,
+ ## we just store a 1.
+ if (defined $specialComponents{$tag}) {
+ my @dirs;
+ foreach my $name (keys %$names) {
+ my $comps = $$names{$name};
+ foreach my $comp (keys %$comps) {
+ foreach my $item (@{$$comps{$comp}}) {
+ if (-d $item) {
+ push(@dirs, $item);
+ }
+ else {
+ @dirs = ();
+ last;
+ }
+ }
+ }
+ }
+ if (defined $dirs[0]) {
+ $self->{'special_supplied'}->{$tag} = \@dirs;
+ }
+ else {
+ $self->{'special_supplied'}->{$tag} = 1;
+ }
+ }
+
+ ## If we didn't encounter an error, didn't have any files explicitly
+ ## listed and we attempted to exclude files, then we need to find the
+ ## set of files that don't match the excluded files and add them.
+ if ($status && defined $exclude[0] && defined $grname) {
+ my $alldir = $self->get_assignment('recurse') || $flags{'recurse'};
+ my %checked;
+ my @files;
+ foreach my $exc (@exclude) {
+ my $dname = $self->mpc_dirname($exc);
+ if (!defined $checked{$dname}) {
+ $checked{$dname} = 1;
+ push(@files, $self->generate_default_file_list($dname,
+ \@exclude,
+ undef, $alldir));
+ }
+ }
+
+ $self->sift_files(\@files,
+ $self->{'valid_components'}->{$tag},
+ $self->get_assignment('pch_header'),
+ $self->get_assignment('pch_source'),
+ $tag,
+ $$comps{$grname});
+ }
+
+ return $status, $error;
+}
+
+
+sub parse_verbatim {
+ my($self, $fh, $type, $loc, $add) = @_;
+
+ if (!defined $loc) {
+ return 0, 'You must provide a location parameter to verbatim';
+ }
+
+ ## All types are lower case
+ $type = lc($type);
+
+ if (!defined $self->{'verbatim'}->{$type}) {
+ $self->{'verbatim'}->{$type} = {};
+ }
+
+ ## Instead of always creating a new array for a particular type and
+ ## location, create a new array if there isn't one already or the user
+ ## does not want to add to the existing verbatim settings.
+ $self->{'verbatim'}->{$type}->{$loc} = []
+ if (!$add || !defined $self->{'verbatim'}->{$type}->{$loc});
+ my $array = $self->{'verbatim'}->{$type}->{$loc};
+
+ while(<$fh>) {
+ my $line = $self->preprocess_line($fh, $_);
+
+ ## This is not an error,
+ ## this is the end of the verbatim
+ last if ($line =~ /^}$/);
+ push(@$array, $line);
+ }
+
+ return 1, undef;
+}
+
+
+sub process_feature {
+ my($self, $fh, $names, $parents) = @_;
+ my $status = 1;
+ my $error;
+
+ my $requires = '';
+ my $avoids = '';
+ foreach my $name (@$names) {
+ if ($name =~ /^!\s*(.*)$/) {
+ $avoids .= ' ' if ($avoids ne '');
+ $avoids .= $1;
+ }
+ else {
+ $requires .= ' ' if ($requires ne '');
+ $requires .= $name;
+ }
+ }
+
+ if ($self->check_features($requires, $avoids)) {
+ ## The required features are enabled, so we say that
+ ## a project has been defined and we allow the parser to
+ ## find the data held within the feature.
+ ($status, $error) = $self->begin_project($parents);
+ if ($status) {
+ $self->{'feature_defined'} = 0;
+ $self->{$self->{'type_check'}} = 1;
+ }
+ }
+ else {
+ ## Otherwise, we read in all the lines until we find the
+ ## closing brace for the feature and it appears to the parser
+ ## that nothing was defined.
+ my $curly = 1;
+ while(<$fh>) {
+ my $line = $self->preprocess_line($fh, $_);
+
+ ## This is a very simplistic way of finding the end of
+ ## the feature definition. It will work as long as no spurious
+ ## open curly braces are counted.
+ ++$curly if ($line =~ /{$/);
+ --$curly if ($line =~ /^}/);
+
+ if ($curly == 0) {
+ $self->{'feature_defined'} = 0;
+ last;
+ }
+ }
+ }
+
+ return $status, $error;
+}
+
+
+sub process_array_assignment {
+ my($self, $aref, $type, $array) = @_;
+
+ if (!defined $$aref || $type == 0) {
+ if ($type != -1) {
+ $$aref = $array;
+ }
+ }
+ else {
+ if ($type == 1) {
+ push(@{$$aref}, @$array);
+ }
+ elsif ($type == -1) {
+ my $count = scalar(@{$$aref});
+ for(my $i = 0; $i < $count; ++$i) {
+ if (StringProcessor::fgrep($$aref->[$i], $array)) {
+ splice(@{$$aref}, $i, 1);
+ --$i;
+ --$count;
+ }
+ }
+ }
+ }
+}
+
+
+sub parse_define_custom {
+ my($self, $fh, $tag, $modify) = @_;
+
+ ## Make the tag something _files
+ $tag = lc($tag) . '_files';
+
+ ## We can not have a custom type named "generic"
+ return 0, "$tag is reserved" if ($tag eq $generic_key);
+
+ if (defined $self->{'valid_components'}->{$tag}) {
+ if (!$modify) {
+ return 0, "$tag has already been defined";
+ }
+ }
+ elsif ($modify) {
+ return 0, "$tag has not yet been defined and can not be modified";
+ }
+
+ my $status = 0;
+ my $errorString = "Unable to process $tag";
+
+ ## Update the custom_types assignment
+ $self->process_assignment_add('custom_types', $tag) if (!$modify);
+
+ if (!defined $self->{'matching_assignments'}->{$tag}) {
+ my @keys = keys %custom;
+ push(@keys, @default_matching_assignments);
+ $self->{'matching_assignments'}->{$tag} = \@keys;
+ }
+
+ my $optname;
+ my $inscope = 0;
+ while(<$fh>) {
+ my $line = $self->preprocess_line($fh, $_);
+
+ if ($line eq '') {
+ }
+ elsif ($line =~ /optional\s*\(([^\)]+)\)\s*{/) {
+ $optname = $1;
+ $optname =~ s/^\s+//;
+ $optname =~ s/\s+$//;
+ if (defined $customDefined{$optname} &&
+ ($customDefined{$optname} & 0x08) != 0) {
+ ++$inscope;
+ if ($inscope != 1) {
+ $status = 0;
+ $errorString = 'Can not nest \'optional\' sections';
+ last;
+ }
+ }
+ else {
+ $status = 0;
+ $errorString = "Invalid optional name: $optname";
+ last;
+ }
+ }
+ elsif ($inscope) {
+ if ($line =~ /^}$/) {
+ $optname = undef;
+ --$inscope;
+ }
+ else {
+ if ($line =~ /(\w+)\s*\(([^\)]+)\)\s*(\+)?=\s*(.*)/) {
+ my $name = lc($1);
+ my $opt = $2;
+ my $add = $3;
+ my @val = split(/\s*,\s*/, $4);
+
+ ## Fix $opt spacing
+ $opt =~ s/(\&\&|\|\|)/ $1 /g;
+ $opt =~ s/!\s+/!/g;
+
+ ## Set up the 'optional' hash table
+ if (!$add || !defined $self->{'generated_exts'}->{$tag}->
+ {'optional'}->{$optname}->{$name}->{$opt}) {
+ $self->{'generated_exts'}->{$tag}->
+ {'optional'}->{$optname}->{$name}->{$opt} = \@val;
+ }
+ else {
+ push(@{$self->{'generated_exts'}->{$tag}->{'optional'}->
+ {$optname}->{$name}->{$opt}}, @val);
+ }
+ }
+ else {
+ $status = 0;
+ $errorString = "Unrecognized optional line: $line";
+ last;
+ }
+ }
+ }
+ elsif ($line =~ /^}$/) {
+ $status = 1;
+ $errorString = undef;
+
+ ## Propagate the custom defined values into the mapped values
+ foreach my $key (keys %{$self->{'valid_names'}}) {
+ if (UNIVERSAL::isa($self->{'valid_names'}->{$key}, 'ARRAY')) {
+ my $value = $self->{'generated_exts'}->{$tag}->{
+ $self->{'valid_names'}->{$key}->[1]};
+
+ ## Bypass the process_assignment() defined in this class
+ ## to avoid unwanted keyword mapping.
+ $self->SUPER::process_assignment($key, $value) if (defined $value);
+ }
+ }
+
+ ## Set some defaults (if they haven't already been set)
+ if (!defined $self->{'generated_exts'}->{$tag}->{'pre_filename'}) {
+ $self->{'generated_exts'}->{$tag}->{'pre_filename'} = [ '' ];
+ }
+ if (!defined $self->{'generated_exts'}->{$tag}->{'pre_dirname'}) {
+ $self->{'generated_exts'}->{$tag}->{'pre_dirname'} = [ '' ];
+ }
+ if (!defined $self->{'generated_exts'}->{$tag}->{'pre_extension'}) {
+ $self->{'generated_exts'}->{$tag}->{'pre_extension'} = [ '' ];
+ }
+ if (!defined $self->{'generated_exts'}->{$tag}->{'automatic_in'}) {
+ $self->{'generated_exts'}->{$tag}->{'automatic_in'} = 1;
+ }
+ if (!defined $self->{'generated_exts'}->{$tag}->{'automatic_out'}) {
+ $self->{'generated_exts'}->{$tag}->{'automatic_out'} = 1;
+ }
+ if (!defined $self->{'generated_exts'}->{$tag}->{'output_follows_input'}) {
+ $self->{'generated_exts'}->{$tag}->{'output_follows_input'} = 1;
+ }
+ if (!defined $self->{'valid_components'}->{$tag}) {
+ $self->{'valid_components'}->{$tag} = [];
+ }
+ last;
+ }
+ else {
+ my @values;
+ ## If this returns true, then we've found an assignment
+ if ($self->parse_assignment($line, \@values)) {
+ my($type, $name, $value) = @values;
+ ## The 'automatic' keyword has always contained two distinct
+ ## functions. The first is to automatically add input files of
+ ## the specified extension. And the second is to automatically
+ ## add generated files to the right components. It has now been
+ ## split into separate functionality and we map the 'automatic'
+ ## keyword to the two new ones here.
+ my $ok = 1;
+ my @names = $name eq 'automatic' ?
+ ('automatic_in', 'automatic_out') : $name;
+ foreach $name (@names) {
+ if (defined $customDefined{$name}) {
+ if (($customDefined{$name} & 0x01) != 0) {
+ $value = $self->escape_regex_special($value);
+ my @array = split(/\s*,\s*/, $value);
+ $self->process_array_assignment(
+ \$self->{'valid_components'}->{$tag}, $type, \@array);
+ }
+ else {
+ if (!defined $self->{'generated_exts'}->{$tag}) {
+ $self->{'generated_exts'}->{$tag} = {};
+ }
+ ## Try to convert the value into a relative path
+ $value = $self->relative($value);
+
+ if (($customDefined{$name} & 0x04) != 0) {
+ if ($type == 0) {
+ $self->process_assignment(
+ $name, $value,
+ $self->{'generated_exts'}->{$tag});
+ }
+ elsif ($type == 1) {
+ $self->process_assignment_add(
+ $name, $value,
+ $self->{'generated_exts'}->{$tag});
+ }
+ elsif ($type == -1) {
+ $self->process_assignment_sub(
+ $name, $value,
+ $self->{'generated_exts'}->{$tag});
+ }
+ }
+ else {
+ if (($customDefined{$name} & 0x02) != 0) {
+ ## Transform the name from something outputext to
+ ## something files. We expect this to match the
+ ## names of valid_assignments.
+ $name =~ s/outputext/files/g;
+ }
+
+ ## Get it ready for regular expressions
+ $value = $self->escape_regex_special($value);
+
+ ## Split the value into an array using a comma as the
+ ## separator. If there are no elements in the array we're
+ ## going to add an empty element to the array. This way,
+ ## assignments of blank values are useful.
+ my @array = split(/\s*,\s*/, $value);
+ push(@array, '') if ($#array == -1);
+
+ ## Process the array assignment after adjusting the values
+ $self->process_array_assignment(
+ \$self->{'generated_exts'}->{$tag}->{$name},
+ $type, \@array);
+ }
+ }
+ }
+ else {
+ $ok = 0;
+ $status = 0;
+ $errorString = "Invalid assignment name: '$name'";
+ last;
+ }
+ }
+
+ ## $status is zero until the end of the define custom block, so
+ ## we can't use it for this check.
+ last if (!$ok);
+ }
+ elsif ($line =~ /^keyword\s+(\w+)(?:\s*=\s*(\w+)?)?/) {
+ ## Check for keyword mapping here
+ my $newkey = $1;
+ my $mapkey = $2;
+ if (defined $self->{'valid_names'}->{$newkey}) {
+ $status = 0;
+ $errorString = "Cannot map $newkey onto an " .
+ "existing keyword";
+ last;
+ }
+ elsif (!defined $mapkey) {
+ $self->{'valid_names'}->{$newkey} = 1;
+ }
+ elsif ($newkey ne $mapkey) {
+ if (defined $customDefined{$mapkey}) {
+ $self->{'valid_names'}->{$newkey} = [ $tag, $mapkey ];
+ }
+ else {
+ $status = 0;
+ $errorString = "Cannot map $newkey to an " .
+ "undefined custom keyword: $mapkey";
+ last;
+ }
+ }
+ else {
+ $status = 0;
+ $errorString = "Cannot map $newkey to $mapkey";
+ last;
+ }
+ }
+ else {
+ $status = 0;
+ $errorString = "Unrecognized line: $line";
+ last;
+ }
+ }
+ }
+
+ return $status, $errorString;
+}
+
+
+sub back_to_variable {
+ my($self, $values) = @_;
+ my $cwd = $self->getcwd();
+ my $case_tolerant = $self->case_insensitive();
+ my @values = ();
+
+ ## Get both of the relative value hash maps and put them in an array
+ my @rels = ();
+ my($rel, $how) = $self->get_initial_relative_values();
+ push(@rels, $rel);
+ ($rel, $how) = $self->get_secondary_relative_values();
+ push(@rels, $rel);
+
+ ## Go through each value and try to convert it to a variable setting
+ foreach my $ovalue (@$values) {
+ ## Fix up the value, replacing '.' with the current working
+ ## directory.
+ my $value = $ovalue;
+ $value =~ s/\\/\//g;
+ if ($value eq '.') {
+ $value = $cwd;
+ }
+ else {
+ $value =~ s/^.\//$cwd\//;
+ }
+ my $valuelen = length($value);
+
+ ## Go through each relative value hash map and see if any of the
+ ## values match the value that we're currently inspecting.
+ my $found = undef;
+ foreach my $rel (@rels) {
+ foreach my $key (keys %$rel) {
+ ## Get the relative replacement value and convert back-slashes
+ my $val = $$rel{$key};
+ $val =~ s/\\/\//g;
+
+ ## We only need to check for reverse replacement if the length
+ ## of the value is greater than or equal to the length of our
+ ## replacement value.
+ my $vlen = length($val);
+ if ($valuelen >= $vlen) {
+ ## Cut the string down by the length of the replacement value
+ my $lval = substr($value, 0, $vlen);
+
+ ## Check for equivalence, taking into account file system
+ ## case-insenitivity.
+ if ($case_tolerant) {
+ $found = (lc($lval) eq lc($val));
+ }
+ else {
+ $found = ($lval eq $val);
+ }
+
+ ## If they match, replace the value and save it in our array.
+ if ($found) {
+ substr($value, 0, length($val)) = "\$($key)";
+ push(@values, $value);
+ last;
+ }
+ }
+ }
+
+ ## Once it's been found, there's no reason to continue on through
+ ## the relative hash maps.
+ last if ($found);
+ }
+
+ push(@values, $ovalue) if (!$found);
+ }
+
+ return @values;
+}
+
+
+sub remove_duplicate_addition {
+ my($self, $name, $value, $nval) = @_;
+
+ if (defined $nval) {
+ ## If we are modifying the libs, libpaths, macros or includes
+ ## assignment with either addition or subtraction, we are going to
+ ## perform a little fix on the value to avoid multiple
+ ## libraries and to try to insure the correct linking order
+ if ($name eq 'macros' || $name eq 'libpaths' ||
+ $name eq 'includes' || $name =~ /libs$/ ||
+ index($name, $grouped_key) == 0) {
+ my $allowed = '';
+ my %parts;
+
+ ## Convert the array into keys for a hash table
+ @parts{@{$self->create_array($nval)}} = ();
+
+ ## In order to ensure that duplicates are correctly removed, we
+ ## need to get the modified assignment value before we attempt to
+ ## do so.
+ $value = $self->modify_assignment_value($name, $value);
+ foreach my $val (@{$self->create_array($value)}) {
+ if (!exists $parts{$val}) {
+ ## We need to supply quotes if there is a space in the value or
+ ## a variable. The variable may contain spaces.
+ my $qt = ($val =~ /\s/ || $val =~ /\$\(.+\)/ ? '"' : '');
+ $allowed .= $qt . $val . $qt . ' ';
+ }
+ }
+ $allowed =~ s/\s+$//;
+ return $allowed;
+ }
+ }
+
+ return $value;
+}
+
+
+sub read_template_input {
+ my($self, $tkey) = @_;
+ my $status = 1;
+ my $errorString;
+ my $file;
+ my $tag;
+ my $ti = $self->get_ti_override();
+ my $lang = $self->get_language();
+ my $override;
+
+ if ($self->exe_target()) {
+ if ($self->get_static() == 1) {
+ $tag = 'lib_exe_template_input';
+ ## Check for the TemplateInputReader for the template key provided.
+ if (!defined $self->{$tag}->{$lang}->{$tkey}) {
+ if (defined $$ti{'lib_exe'}) {
+ $file = $$ti{'lib_exe'};
+ $override = 1;
+ }
+ else {
+ $file = $self->get_lib_exe_template_input_file($tkey);
+ }
+ }
+ }
+ else {
+ $tag = 'dll_exe_template_input';
+ ## Check for the TemplateInputReader for the template key provided.
+ if (!defined $self->{$tag}->{$lang}->{$tkey}) {
+ if (defined $$ti{'dll_exe'}) {
+ $file = $$ti{'dll_exe'};
+ $override = 1;
+ }
+ else {
+ $file = $self->get_dll_exe_template_input_file($tkey);
+ }
+ }
+ }
+ }
+ else {
+ if ($self->get_static() == 1) {
+ $tag = 'lib_template_input';
+ ## Check for the TemplateInputReader for the template key provided.
+ if (!defined $self->{$tag}->{$lang}->{$tkey}) {
+ if (defined $$ti{'lib'}) {
+ $file = $$ti{'lib'};
+ $override = 1;
+ }
+ else {
+ $file = $self->get_lib_template_input_file($tkey);
+ }
+ }
+ }
+ else {
+ $tag = 'dll_template_input';
+ ## Check for the TemplateInputReader for the template key provided.
+ if (!defined $self->{$tag}->{$lang}->{$tkey}) {
+ if (defined $$ti{'dll'}) {
+ $file = $$ti{'dll'};
+ $override = 1;
+ }
+ else {
+ $file = $self->get_dll_template_input_file($tkey);
+ }
+ }
+ }
+ }
+
+ if (defined $self->{$tag}->{$lang}->{$tkey}) {
+ ## We have a TemplateInputReader for this template key, so we need
+ ## to set the entry corresponding to $tikey to it for use in the
+ ## get_template_input() method.
+ $self->{$tag}->{$lang}->{$tikey} = $self->{$tag}->{$lang}->{$tkey};
+ }
+ else {
+ ## We haven't read this file yet, so we will create the template
+ ## input reader and store it in the entry for the template key
+ ## ($tkey) and the template input key ($tikey).
+ my $ti = new TemplateInputReader($self->get_include_path());
+ $self->{$tag}->{$lang}->{$tkey} = $ti;
+ $self->{$tag}->{$lang}->{$tikey} = $ti;
+
+ ## Process the template input file
+ if (defined $file) {
+ my $tfile = $self->search_include_path("$file.$TemplateInputExtension");
+ if (defined $tfile) {
+ ($status, $errorString) = $ti->read_file($tfile);
+ }
+ else {
+ ## Not finding a template input file is only an error if the user
+ ## specifically provided a template input file override.
+ if ($override) {
+ $status = 0;
+ $errorString = "Unable to locate template input file: $file";
+ }
+ }
+ }
+
+ ## Now that we've read in the template input file, set up our
+ ## automatic template variables.
+ if ($self->{'make_coexistence'}) {
+ $ti->parse_line(undef,
+ "make_coexistence = $self->{'make_coexistence'}");
+ }
+ }
+
+ ## We do this regardless of whether or not this parser is cached or
+ ## not. If the features have changed (through a workspace cmdline
+ ## setting), we need to reflect it.
+ if ($status) {
+ ## Put the features into the template input set
+ my $features = $self->{'feature_parser'}->get_names();
+ $self->{$tag}->{$lang}->{$tikey}->parse_line(undef,
+ "features = @$features");
+ }
+
+ return $status, $errorString;
+}
+
+
+sub already_added {
+ my($self, $array, $name) = @_;
+
+ ## This method expects that the file name will be unix style
+ $name =~ s/\\/\//g if ($self->{'convert_slashes'});
+
+ ## Remove the leading ./
+ $name =~ s/^\.\///;
+ my $dsname = "./$name";
+
+ foreach my $file (@$array) {
+ return 1 if ($file eq $name || $file eq $dsname);
+ }
+
+ return 0;
+}
+
+
+sub get_applied_custom_keyword {
+ my($self, $name, $type, $file) = @_;
+
+ if (defined $self->{'flag_overrides'}->{$type} &&
+ defined $self->{'flag_overrides'}->{$type}->{$file} &&
+ defined $self->{'flag_overrides'}->{$type}->{$file}->{$name}) {
+ return $self->relative(
+ $self->{'flag_overrides'}->{$type}->{$file}->{$name}, 1);
+ }
+
+ return $self->relative($self->get_assignment(
+ $name,
+ $self->{'generated_exts'}->{$type}), 1);
+}
+
+
+sub evaluate_optional_option {
+ my($self, $opt, $value) = @_;
+
+ if ($opt =~ /^!\s*(.*)/) {
+ return (!exists $$value{$1} ? 1 : 0);
+ }
+ else {
+ return (exists $$value{$opt} ? 1 : 0);
+ }
+}
+
+
+sub process_optional_option {
+ my($self, $opt, $value) = @_;
+ my $status;
+ my @parts = grep(!/^$/, split(/\s+/, $opt));
+ my $pcount = scalar(@parts);
+
+ for(my $i = 0; $i < $pcount; $i++) {
+ if ($parts[$i] eq '&&' || $parts[$i] eq '||') {
+ if (defined $status) {
+ if (defined $parts[$i + 1]) {
+ if ($parts[$i] eq '&&') {
+ $status &&= $self->evaluate_optional_option($parts[$i + 1],
+ $value);
+ }
+ else {
+ ## We are coming into an '||', if status is already true
+ ## then we can leave immediately
+ last if ($status);
+
+ $status ||= $self->evaluate_optional_option($parts[$i + 1],
+ $value);
+ }
+ }
+ else {
+ $self->warning("Expected token in optional after $parts[$i]");
+ }
+ }
+ else {
+ $self->warning("Unexpected token in optional: $parts[$i]");
+ }
+ ++$i;
+ }
+ else {
+ if (!defined $status) {
+ $status = $self->evaluate_optional_option($parts[$i], $value);
+ }
+ else {
+ $self->warning("Unexpected token in optional: $parts[$i]");
+ }
+ }
+ }
+
+ return $status;
+}
+
+
+sub add_optional_filename_portion {
+ my($self, $gentype, $tag, $file, $array) = @_;
+
+ if (defined $self->{'generated_exts'}->{$gentype}->{'optional'}->{$tag}) {
+ foreach my $name (keys %{$self->{'generated_exts'}->{$gentype}->{'optional'}->{$tag}}) {
+ foreach my $opt (keys %{$self->{'generated_exts'}->{$gentype}->{'optional'}->{$tag}->{$name}}) {
+ ## Get the name value
+ my $value = $self->get_applied_custom_keyword($name,
+ $gentype, $file);
+
+ ## Convert the value into a hash map for easy lookup
+ my %values;
+ @values{split(/\s+/, $value)} = () if (defined $value);
+
+ ## See if the option or options are contained in the value. We
+ ## need to call this even if $value is not defined due to the
+ ## ability to negate optional parameters.
+ if ($self->process_optional_option($opt, \%values)) {
+ ## Add the optional portion
+ push(@$array, @{$self->{'generated_exts'}->{$gentype}->{'optional'}->{$tag}->{$name}->{$opt}});
+ }
+ }
+ }
+ }
+}
+
+
+sub get_pre_keyword_array {
+ my($self, $keyword, $gentype, $tag, $file) = @_;
+
+ ## Get the general pre extension array.
+ ## $self->{'generated_exts'}->{$gentype}->{$keyword} is guaranteed to
+ ## be defined due to the defaulting that is done in
+ ## parse_define_custom() and the only three calls to this method use
+ ## valid $keyword values.
+ my @array = @{$self->{'generated_exts'}->{$gentype}->{$keyword}};
+
+ ## Add the component specific pre extension array
+ my @additional;
+ $tag =~ s/files$/$keyword/;
+ if (defined $self->{'generated_exts'}->{$gentype}->{$tag}) {
+ push(@additional, @{$self->{'generated_exts'}->{$gentype}->{$tag}});
+ }
+
+ ## Add in any optional portion to the array
+ foreach my $itag ($keyword, $tag) {
+ $self->add_optional_filename_portion($gentype, $itag,
+ $file, \@additional);
+ }
+
+ ## If the current array only has the default,
+ ## then we need to remove it
+ if (defined $additional[0]) {
+ if ($#array == 0 && $array[0] eq '') {
+ pop(@array);
+ }
+ push(@array, @additional);
+ }
+
+ return @array;
+}
+
+
+sub add_explicit_output {
+ my($self, $file, $type, $tag, $array, $arrs) = @_;
+
+ if (defined $self->{'custom_special_output'}->{$type} &&
+ defined $self->{'custom_special_output'}->{$type}->{$file}) {
+ if (defined $self->{'valid_components'}->{$tag}) {
+ my @files;
+ foreach my $check (@{$self->{'custom_special_output'}->{$type}->{$file}}) {
+ foreach my $regext (@{$self->{'valid_components'}->{$tag}}) {
+ if ($check =~ /$regext$/) {
+ my $add = 1;
+ if ($tag eq 'source_files') {
+ foreach my $tregext (@{$self->{'valid_components'}->{'template_files'}}) {
+ if ($check =~ /$tregext$/) {
+ $add = undef;
+ last;
+ }
+ }
+ }
+ if ($add) {
+ ## If gendir was specified, then we need to account for that
+ my $dir = '';
+ if (defined $self->{'flag_overrides'}->{$type} &&
+ defined $self->{'flag_overrides'}->{$type}->{$file} &&
+ defined $self->{'flag_overrides'}->{$type}->{$file}->{'gendir'} &&
+ $self->{'flag_overrides'}->{$type}->{$file}->{'gendir'} ne '.') {
+ $dir = $self->{'flag_overrides'}->{$type}->{$file}->{'gendir'} . '/';
+ $dir =~ s/\\/\//g if ($self->{'convert_slashes'});
+ }
+
+ push(@files, "$dir$check");
+ last;
+ }
+ }
+ }
+ }
+ if (defined $files[0]) {
+ if ($arrs) {
+ push(@$array, \@files);
+ }
+ else {
+ push(@$array, @files);
+ }
+ }
+ }
+ }
+}
+
+sub generated_filenames {
+ my($self, $part, $type, $tag, $file, $noext, $arrs) = @_;
+
+ ## A custom type is not allowed to generate it's own input files
+ return () if ($type eq $tag);
+
+ ## See if the type for which we are generating ($tag) is also a custom
+ ## file type. If it is, we need to do some massaging.
+ my $otag = $tag;
+ if (defined $self->{'generated_exts'}->{$tag}) {
+ ## If the custom type ($type) doesn't specify that it generates
+ ## generic files, we need to see if there is a command helper for
+ ## this type and see what sort of output it knows about.
+ my $inputexts = $self->{'generated_exts'}->{$type}->{$generic_key};
+ if (!defined $inputexts) {
+ my $cmdHelper = CommandHelper::get($type);
+ $inputexts = $cmdHelper->get_outputexts() if (defined $cmdHelper);
+ }
+
+ ## We will need to use 'generic_files' instead of $tag if $tag is
+ ## defined in 'generated_exts', but only for the type that will
+ ## actually generate the right type of generic file.
+ my $good;
+ if (defined $inputexts) {
+ foreach my $inputext (@$inputexts) {
+ my $ext = $inputext;
+ $ext =~ s/\\//g;
+ foreach my $extreg (@{$self->{'valid_components'}->{$tag}}) {
+ if ($ext =~ /$extreg$/) {
+ $tag = $generic_key;
+ $good = 1;
+ last;
+ }
+ }
+ last if ($good);
+ }
+ }
+ return () if (!$good);
+ }
+
+ my @pearr = $self->get_pre_keyword_array('pre_extension',
+ $type, $tag, $file);
+ my @pfarr = $self->get_pre_keyword_array('pre_filename',
+ $type, $tag, $file);
+ my @pdarr = $self->get_pre_keyword_array('pre_dirname',
+ $type, $tag, $file);
+ my @exts = (defined $self->{'generated_exts'}->{$type}->{$tag} ?
+ @{$self->{'generated_exts'}->{$type}->{$tag}} : ());
+
+ if (!defined $exts[0]) {
+ my $backtag = $tag;
+ if ($backtag =~ s/files$/outputext/) {
+ $self->add_optional_filename_portion($type, $backtag,
+ $file, \@exts);
+ }
+ }
+
+ my @array;
+ if (!defined $exts[0] && $#pearr == 0 && $#pfarr == 0 && $#pdarr == 0 &&
+ $pearr[0] eq '' && $pfarr[0] eq '' && $pdarr[0] eq '') {
+ ## If both arrays are defined to be the defaults, then there
+ ## is nothing for us to do.
+ }
+ else {
+ my $dir = '';
+ my $base;
+
+ ## Correctly deal with pre filename and directories
+ if ($part =~ /(.*[\/\\])([^\/\\]+)$/) {
+ ## Split the directory and base name of the file. Only set the
+ ## directory if the output follows the input directory.
+ $dir = $1
+ if ($self->{'generated_exts'}->{$type}->{'output_follows_input'});
+ $base = $2;
+ }
+ else {
+ $base = $part;
+ }
+
+ ## If gendir was specified, then we need to account for that
+ if (defined $self->{'flag_overrides'}->{$type} &&
+ defined $self->{'flag_overrides'}->{$type}->{$file} &&
+ defined $self->{'flag_overrides'}->{$type}->{$file}->{'gendir'}) {
+ if ($self->{'flag_overrides'}->{$type}->{$file}->{'gendir'} eq '.') {
+ $dir = '';
+ }
+ else {
+ $dir = $self->{'flag_overrides'}->{$type}->{$file}->{'gendir'} . '/';
+ $dir =~ s/\\/\//g if ($self->{'convert_slashes'});
+ }
+ }
+
+ ## Loop through creating all of the possible file names
+ foreach my $pe (@pearr) {
+ my @genfile;
+ $pe =~ s/\\\././g;
+ foreach my $pf (@pfarr) {
+ $pf =~ s/\\\././g;
+ foreach my $pd (@pdarr) {
+ if ($noext) {
+ push(@genfile, "$pd$dir$pf$base$pe");
+ }
+ else {
+ foreach my $ext (@exts) {
+ $ext =~ s/\\\././g;
+ push(@genfile, "$pd$dir$pf$base$pe$ext");
+ }
+ }
+ }
+ }
+ if ($arrs) {
+ push(@array, \@genfile);
+ }
+ else {
+ push(@array, @genfile);
+ }
+ }
+ }
+
+ ## Now add the explicit output. We need to use the original tag value
+ ## ($otag) so that we can find the custom output files.
+ $self->add_explicit_output($file, $type, $otag, \@array, $arrs);
+ return @array;
+}
+
+
+sub add_generated_files {
+ my($self, $gentype, $tag, $group, $arr) = @_;
+
+ ## This method is called by list_default_generated. It performs the
+ ## actual file insertion and grouping.
+
+ ## Get the generated filenames
+ my @added;
+ foreach my $file (keys %$arr) {
+ foreach my $gen ($self->generated_filenames($$arr{$file}, $gentype,
+ $tag, $file, 1)) {
+ $self->list_generated_file($gentype, $tag, \@added, $gen, $$arr{$file});
+ }
+ }
+
+ if (defined $added[0]) {
+ my $names = $self->{$tag};
+
+ ## Get all files in one list and save the directory
+ ## and component group in a hashed array.
+ my @all;
+ my %dircomp;
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$$names{$name}}) {
+ push(@all, @{$$names{$name}->{$key}});
+ foreach my $file (@{$$names{$name}->{$key}}) {
+ $dircomp{$self->mpc_dirname($file)} = $key;
+ }
+ }
+ }
+
+ ## Create a small array of only the files we want to add.
+ ## We put them all together so we can keep them in order when
+ ## we put them at the front of the main file list.
+ my @oktoadd;
+ foreach my $file (@added) {
+ push(@oktoadd, $file) if (!$self->already_added(\@all, $file));
+ }
+
+ ## If we have files to add, make sure we add them to a group
+ ## that has the same directory location as the files we're adding.
+ if (defined $oktoadd[0]) {
+ my $key = (defined $group ? $group :
+ $dircomp{$self->mpc_dirname($oktoadd[0])});
+ if (!defined $key) {
+ my $check = $oktoadd[0];
+ foreach my $regext (@{$self->{'valid_components'}->{$tag}}) {
+ last if ($check =~ s/$regext$//);
+ }
+ foreach my $vc (keys %{$self->{'valid_components'}}) {
+ ## If this component name does not match the component name for
+ ## which we are adding files and there are components defined
+ ## for it, we will look to see if we can find a matching group
+ ## name. We have to make sure that we do not use the hash map
+ ## ($self->{$vc}) unless it's defined. Doing so will
+ ## automatically create the map and that will cause MPC to
+ ## think that the user provided the empty setting (when it
+ ## wasn't).
+ if ($vc ne $tag && defined $self->{$vc}) {
+ foreach my $name (keys %{$self->{$vc}}) {
+ foreach my $ckey (keys %{$self->{$vc}->{$name}}) {
+ if ($ckey ne $defgroup) {
+ foreach my $ofile (@{$self->{$vc}->{$name}->{$ckey}}) {
+ my $file = $ofile;
+ foreach my $regext (@{$self->{'valid_components'}->{$vc}}) {
+ last if ($file =~ s/$regext$//);
+ }
+ if ($file eq $check) {
+ $key = $ckey;
+ last;
+ }
+ }
+ }
+ last if (defined $key);
+ }
+ }
+ last if (defined $key);
+ }
+ }
+ $key = $defgroup if (!defined $key);
+ }
+ foreach my $name (keys %$names) {
+ if (!defined $$names{$name}->{$key}) {
+ if ($key ne $defgroup &&
+ defined $$names{$name}->{$defgroup} &&
+ defined $$names{$name}->{$defgroup}->[0]) {
+ $self->process_assignment_add($grouped_key . $tag, $defgroup);
+ }
+ $$names{$name}->{$key} = [];
+ $self->process_assignment_add($grouped_key . $tag, $key);
+ }
+ unshift(@{$$names{$name}->{$key}}, @oktoadd);
+ }
+ }
+ }
+}
+
+
+sub search_for_entry {
+ my($self, $file, $marray, $preproc) = @_;
+ my $name;
+ my $fh = new FileHandle();
+
+ if (open($fh, $file)) {
+ my $poundifed = 0;
+ my $commented = 0;
+
+ while(<$fh>) {
+ ## Remove c++ style comments
+ $_ =~ s/\/\/.*// if (!$commented);
+
+ ## Remove one line c style comments
+ $_ =~ s/\/\*.*\*\///g;
+
+ if ($commented) {
+ if (/\*\//) {
+ ## Found the end of a multi-line c style comment
+ --$commented;
+ }
+ }
+ else {
+ if (/\/\*/) {
+ ## Found the beginning of a multi-line c style comment
+ ++$commented;
+ }
+ elsif ($preproc) {
+ ## If the current language supports a c preprocessor, we
+ ## will perform a minimal check for #if 0
+ if (/#\s*if\s+0/) {
+ ## Found the beginning of a #if 0
+ ++$poundifed;
+ }
+ elsif ($poundifed) {
+ if (/#\s*if/) {
+ ## We need to keep track of any other #if directives
+ ## to be sure that when we see an #endif we don't
+ ## count the wrong one.
+ ++$poundifed;
+ }
+ elsif (/#\s*endif/) {
+ ## Found a #endif, so decrement our count
+ --$poundifed;
+ }
+ }
+ }
+ }
+
+ ## Check for main; Make sure it's not #if 0'ed and not commented out
+ if (!$poundifed && !$commented) {
+ my $found = undef;
+ foreach my $main (@$marray) {
+ if (/\s+$main\s*\(/ || /^\s*$main\s*\(/) {
+ ## If we've found a main, set the exename to the basename
+ ## of the cpp file with the extension removed
+ $name = $self->mpc_basename($file);
+ $name =~ s/\.[^\.]+$//;
+ $found = 1;
+ last;
+ }
+ last if ($found);
+ }
+ }
+ }
+ close($fh);
+ }
+ return $name;
+}
+
+
+sub find_main_file {
+ my($self, $sources) = @_;
+ my $lang = $self->get_language();
+ my @main = $language{$lang}->[3];
+ my $preproc = $language{$lang}->[4];
+
+ ## If additional main's have been supplied by the user for this
+ ## language type, then just push them onto the array.
+ push(@main, @{$mains{$lang}}) if (defined $mains{$lang});
+
+ ## Now search each source file until we've found a main function.
+ foreach my $file (@$sources) {
+ my $exename = $self->search_for_entry($file, \@main, $preproc);
+ return $exename if (defined $exename);
+ }
+
+ return undef;
+}
+
+
+sub generate_default_target_names {
+ my $self = shift;
+
+ ## If this is a custom_only project, we need not waste time setting the
+ ## sharedname, staticname or exename. Searching all of the files for a
+ ## main function is very time consuming and unnecessary.
+ return undef if ($self->get_assignment('custom_only'));
+
+ if (!$self->exe_target()) {
+ my $sharedname = $self->get_assignment('sharedname');
+ my $staticname = $self->get_assignment('staticname');
+ my $shared_empty;
+
+ if (defined $sharedname) {
+ if ($sharedname eq '') {
+ $shared_empty = 1;
+ $sharedname = undef;
+ $self->process_assignment('sharedname', $sharedname);
+ }
+ elsif (!defined $staticname) {
+ $staticname = $sharedname;
+ $self->process_assignment('staticname', $staticname);
+ }
+ }
+ if (defined $staticname && !$shared_empty && !defined $sharedname) {
+ $sharedname = $staticname;
+ $self->process_assignment('sharedname', $sharedname);
+ }
+
+ ## If it's neither an exe or library target, we will search
+ ## through the source files for a main()
+ if (!$self->lib_target()) {
+ ## Set the exename assignment
+ my @sources = $self->get_component_list('source_files', 1);
+ my $exename = $self->find_main_file(\@sources);
+ $self->process_assignment('exename', $exename) if (defined $exename);
+
+ ## If we still don't have a project type, then we will
+ ## default to a library if there are source or resource files
+ if (!defined $exename) {
+ if (!defined $sources[0]) {
+ @sources = $self->get_component_list($self->get_resource_tag(), 1);
+ }
+ if (defined $sources[0]) {
+ if (!$shared_empty) {
+ $self->process_assignment('sharedname',
+ $self->{'unmodified_project_name'});
+ }
+ $self->process_assignment('staticname',
+ $self->{'unmodified_project_name'});
+ }
+ }
+ }
+ }
+
+ ## If we are generating only static projects, then we need to
+ ## unset the sharedname, so that we can insure that projects of
+ ## various types only generate static targets.
+ if ($self->get_static() == 1) {
+ my $sharedname = $self->get_assignment('sharedname');
+ if (defined $sharedname) {
+ $self->process_assignment('sharedname', undef);
+ }
+ }
+
+ ## Check for the use of an asterisk in the name
+ foreach my $key ('exename', 'sharedname', 'staticname') {
+ my $value = $self->get_assignment($key);
+ if (defined $value && index($value, '*') >= 0) {
+ $value = $self->fill_type_name($value,
+ $self->{'unmodified_project_name'});
+ $self->process_assignment($key, $value);
+ }
+ }
+}
+
+
+sub generate_default_pch_filenames {
+ my($self, $files) = @_;
+ my $pchhdef = (defined $self->get_assignment('pch_header'));
+ my $pchcdef = (defined $self->get_assignment('pch_source'));
+
+ if (!$pchhdef || !$pchcdef) {
+ my $pname = $self->get_assignment('project_name');
+ my $hcount = 0;
+ my $ccount = 0;
+ my $hmatching;
+ my $cmatching;
+ foreach my $file (@$files) {
+ ## If the file doesn't even contain _pch, then there's no point
+ ## in looping through all of the extensions
+ if (index($file, '_pch') >= 0) {
+ if (!$pchhdef) {
+ foreach my $ext (@{$self->{'valid_components'}->{'header_files'}}) {
+ if ($file =~ /(.*_pch$ext)$/) {
+ $self->process_assignment('pch_header', $1);
+ ++$hcount;
+ $hmatching = $file if (index($file, $pname) >= 0);
+ last;
+ }
+ }
+ }
+ if (!$pchcdef) {
+ foreach my $ext (@{$self->{'valid_components'}->{'source_files'}}) {
+ if ($file =~ /(.*_pch$ext)$/) {
+ $self->process_assignment('pch_source', $1);
+ ++$ccount;
+ $cmatching = $file if (index($file, $pname) >= 0);
+ last;
+ }
+ }
+ }
+ }
+ }
+ if (!$pchhdef && $hcount > 1 && defined $hmatching) {
+ $self->process_assignment('pch_header', $hmatching);
+ }
+ if (!$pchcdef && $ccount > 1 && defined $cmatching) {
+ $self->process_assignment('pch_source', $cmatching);
+ }
+ }
+}
+
+
+sub fix_pch_filenames {
+ my $self = shift;
+
+ ## Unset the precompiled header settings if they are set but empty
+ foreach my $type ('pch_header', 'pch_source') {
+ my $pch = $self->get_assignment($type);
+ $self->process_assignment($type, undef) if (defined $pch && $pch eq '');
+ }
+}
+
+
+sub remove_extra_pch_listings {
+ my $self = shift;
+ my @pchs = ('pch_header', 'pch_source');
+ my @tags = ('header_files', 'source_files');
+
+ for(my $j = 0; $j < 2; ++$j) {
+ my $pch = $self->get_assignment($pchs[$j]);
+
+ if (defined $pch) {
+ ## If we are converting slashes, then we need to
+ ## convert the pch file back to forward slashes
+ $pch =~ s/\\/\//g if ($self->{'convert_slashes'});
+
+ ## Find out which files are duplicated
+ my $names = $self->{$tags[$j]};
+ foreach my $name (keys %$names) {
+ my $comps = $$names{$name};
+ foreach my $key (keys %$comps) {
+ my $array = $$comps{$key};
+ my $count = scalar(@$array);
+ for(my $i = 0; $i < $count; ++$i) {
+ if ($pch eq $$array[$i]) {
+ splice(@$array, $i, 1);
+ --$count;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+sub sift_files {
+ my($self, $files, $exts, $pchh, $pchc, $tag, $array, $alldir) = @_;
+ my @saved;
+ my $havec = (defined $self->{'exclude_components'}->{$tag});
+
+ ## The special actions taken based on $saverc only applies to
+ ## C++ resource files.
+ my $saverc = (!$alldir && $tag eq $self->get_resource_tag() &&
+ $self->languageIs(Creator::cplusplus));
+
+ foreach my $ext (@$exts) {
+ foreach my $file (grep(/$ext$/, @$files)) {
+ ## Always exclude the precompiled header and cpp
+ if ((!defined $pchh || $file ne $pchh) &&
+ (!defined $pchc || $file ne $pchc)) {
+ if ($havec) {
+ my $exclude = 0;
+ foreach my $exc (@{$self->{'exclude_components'}->{$tag}}) {
+ if ($file =~ /$exc$/) {
+ $exclude = 1;
+ last;
+ }
+ }
+ next if ($exclude);
+ }
+ elsif ($saverc) {
+ ## Save these files for later. There may
+ ## be more than one and we want to try and
+ ## find the one that corresponds to this project
+ push(@saved, $file);
+ next;
+ }
+
+ push(@$array, $file) if (!$self->already_added($array, $file));
+ }
+ }
+ }
+
+ ## Now deal with the saved files
+ if (defined $saved[0]) {
+ if (!defined $saved[1]) {
+ ## Theres only one rc file, take it
+ push(@$array, $saved[0]);
+ }
+ else {
+ my $pjname = $self->escape_regex_special(
+ $self->transform_file_name(
+ $self->get_assignment('project_name')));
+ ## Use a case insensitive search.
+ ## After all, this is a Windows specific file type.
+ foreach my $save (@saved) {
+ if ($save =~ /$pjname/i) {
+ if (!$self->already_added($array, $save)) {
+ push(@$array, $save);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+sub sift_default_file_list {
+ my($self, $tag, $file, $built, $exts, $recurse, $pchh, $pchc) = @_;
+ my $alldir = $recurse ||
+ (defined $self->{'flag_overrides'}->{$tag} &&
+ defined $self->{'flag_overrides'}->{$tag}->{$file} &&
+ $self->{'flag_overrides'}->{$tag}->{$file}->{'recurse'});
+ my @gen = $self->generate_default_file_list($file, [], undef, $alldir);
+
+ $self->sift_files(\@gen, $exts, $pchh, $pchc, $tag, $built, $alldir);
+}
+
+
+sub correct_generated_files {
+ my($self, $defcomp, $exts, $tag, $array) = @_;
+
+ if (defined $sourceComponents{$tag}) {
+ my $grtag = $grouped_key . $tag;
+ foreach my $gentype (keys %{$self->{'generated_exts'}}) {
+ ## If we are not automatically adding generated output, then we
+ ## need to skip this component type.
+ next if (!$self->{'generated_exts'}->{$gentype}->{'automatic_out'});
+
+ ## If we are auto-generating the source_files, then
+ ## we need to make sure that any generated source
+ ## files that are added are put at the front of the list.
+ my $newgroup;
+ my @input;
+
+ ## If I call keys %{$self->{$gentype}} using perl 5.6.1
+ ## it returns nothing. I have to put it in an
+ ## intermediate variable to ensure that I get the keys.
+ my $names = $self->{$gentype};
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$$names{$name}}) {
+ push(@input, @{$$names{$name}->{$key}});
+ $newgroup = $key if ($key ne $defgroup);
+ }
+ }
+
+ if (defined $input[0]) {
+ my @front;
+ my @copy = @$array;
+
+ @$array = ();
+ foreach my $input (@input) {
+ my $part = $self->remove_wanted_extension(
+ $input,
+ $self->{'valid_components'}->{$gentype});
+
+ my @files = $self->generated_filenames($part, $gentype,
+ $tag, $input);
+ if (defined $copy[0]) {
+ my $found = 0;
+ foreach my $file (@files) {
+ for(my $i = 0; $i < scalar(@copy); $i++) {
+ my $re = $self->escape_regex_special($copy[$i]);
+ if ($file eq $copy[$i] || $file =~ /[\/\\]$re$/) {
+ ## No need to check for previously added files
+ ## here since there are none.
+ $found = 1;
+ push(@front, $file);
+ splice(@copy, $i, 1);
+ last;
+ }
+ }
+ last if ($found);
+ }
+ if (!$found) {
+ ## The first file listed in @files is the preferred
+ ## extension for the custom command. Take the first
+ ## file extension and see if it matches one in the accepted
+ ## extensions.
+ if (defined $files[0]) {
+ my $ext;
+ if ($files[0] =~ /.*(\.[^\.]+)$/) {
+ $ext = $self->escape_regex_special($1);
+ }
+ if (defined $ext) {
+ ## If it doesn't match one of the accepted extensions,
+ ## then just use the first extension from the type for
+ ## which we are generating.
+ $ext = $$exts[0] if (!StringProcessor::fgrep($ext, $exts));
+ }
+
+ ## Add all the files that match the chosen extension
+ foreach my $file (@files) {
+ push(@front, $file) if ($file =~ /$ext$/);
+ }
+ }
+ }
+ }
+ else {
+ my $ext = $$exts[0];
+ foreach my $file (@files) {
+ push(@front, $file) if ($file =~ /$ext$/);
+ }
+ }
+ }
+ if (defined $copy[0]) {
+ ## No need to check for previously added files
+ ## here since there are none.
+ push(@$array, @copy);
+ if (defined $self->get_assignment($grtag)) {
+ $self->process_assignment_add($grtag, $defgroup);
+ }
+ }
+ if (defined $front[0]) {
+ if (defined $newgroup) {
+ if (defined $copy[0]) {
+ $self->process_assignment_add($grtag, $defgroup);
+ }
+ if (!defined $self->{$tag}->{$defcomp}->{$newgroup}) {
+ $self->{$tag}->{$defcomp}->{$newgroup} = \@front;
+ }
+ else {
+ push(@{$self->{$tag}->{$defcomp}->{$newgroup}}, @front);
+ }
+ $self->process_assignment_add($grtag, $newgroup);
+ }
+ else {
+ unshift(@$array, @front);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+sub generate_default_components {
+ my($self, $files, $passed) = @_;
+ my $genext = $self->{'generated_exts'};
+ my @gc = reverse sort { $self->sort_generated_types($a, $b)
+ } keys %$genext;
+ my @tags = (defined $passed ? $passed :
+ (@gc, keys %{$language{$self->get_language()}->[0]}));
+ my $pchh = $self->get_assignment('pch_header');
+ my $pchc = $self->get_assignment('pch_source');
+ my $recurse = $self->get_assignment('recurse');
+ my $defcomp = $self->get_default_component_name();
+ my $flo = $self->{'flag_overrides'};
+ my $cmdflags = 'commandflags';
+
+ ## The order of @tags does make a difference in the way that generated
+ ## files get added. Hence the sort call on the generate_exts keys to
+ ## ensure that user defined types come first. They are reverse sorted
+ ## using the custom sort function to ensure that user defined types
+ ## that rely on other user defined types for input files are processed
+ ## first.
+ foreach my $tag (@tags) {
+ if (!defined $genext->{$tag} ||
+ $genext->{$tag}->{'automatic_in'}) {
+ my $exts = $self->{'valid_components'}->{$tag};
+ if (defined $$exts[0]) {
+ if (defined $self->{$tag}) {
+ ## If the tag is defined, then process directories
+ my $names = $self->{$tag};
+ foreach my $name (keys %$names) {
+ my $comps = $$names{$name};
+ foreach my $comp (keys %$comps) {
+ my $array = $$comps{$comp};
+ if (defined $passed) {
+ $self->sift_files($files, $exts, $pchh, $pchc, $tag, $array);
+ }
+ else {
+ my @built;
+ my $alldirs = 1;
+ foreach my $file (@$array) {
+ if (-d $file) {
+ my @portion;
+ $self->sift_default_file_list($tag, $file, \@portion,
+ $exts, $recurse, $pchh, $pchc);
+
+ ## Since the file was actually a directory, we will
+ ## need to propagate the flag overrides (if there are
+ ## any) to the newly located files.
+ if (defined $flo->{$tag} &&
+ defined $flo->{$tag}->{$file}) {
+ foreach my $built (@portion) {
+ $flo->{$tag}->{$built} = $flo->{$tag}->{$file};
+ }
+ }
+
+ ## Always push the @portion array onto the back of
+ ## @built.
+ push(@built, @portion);
+ }
+ else {
+ $alldirs = undef;
+ if (!$self->already_added(\@built, $file)) {
+ push(@built, $file);
+ }
+ }
+ }
+ if ($alldirs) {
+ $self->correct_generated_files($defcomp, $exts,
+ $tag, \@built);
+ }
+ $$comps{$comp} = \@built;
+ }
+ }
+ }
+ }
+ else {
+ ## Generate default values for undefined tags
+ $self->{$tag} = {};
+ my $comps = {};
+ $self->{$tag}->{$defcomp} = $comps;
+ $$comps{$defgroup} = [];
+ my $array = $$comps{$defgroup};
+
+ $self->{'defaulted'}->{$tag} = 1;
+
+ if (!defined $specialComponents{$tag}) {
+ $self->sift_files($files, $exts, $pchh, $pchc, $tag, $array);
+ $self->correct_generated_files($defcomp, $exts, $tag, $array);
+ }
+ }
+
+ ## If the type that we're generating defaults for ($tag) is a
+ ## custom type, then we need to see if other custom types
+ ## ($gentype) will generate files that will be used as input. It
+ ## has to be done here so that the built-in types will have all
+ ## of the possible input files that they can.
+ if (defined $genext->{$tag}) {
+ foreach my $gentype (keys %{$genext}) {
+ if ($gentype ne $tag) {
+ $self->list_default_generated($gentype, [$tag]);
+ }
+ }
+
+ ## Now that we have the files for this type ($tag), we need to
+ ## locate a command helper for the custom command and see if it
+ ## knows about any additional output files based on the file
+ ## name.
+ my $cmdHelper = CommandHelper::get($tag);
+ if (defined $cmdHelper) {
+ my $names = $self->{$tag};
+ foreach my $name (keys %$names) {
+ my $comps = $$names{$name};
+ foreach my $comp (keys %$comps) {
+ my $array = $$comps{$comp};
+ foreach my $file (@$array) {
+ my $flags = defined $flo->{$tag}->{$file} ?
+ $flo->{$tag}->{$file}->{$cmdflags} :
+ $genext->{$tag}->{$cmdflags};
+ my $add_out = $cmdHelper->get_output($file, $flags);
+ push(@{$self->{'custom_special_output'}->{$tag}->{$file}},
+ @$add_out);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+sub remove_duplicated_files {
+ my($self, $dest, $source) = @_;
+ my @slist = $self->get_component_list($source, 1);
+
+ ## There's no point in going on if there's nothing in this component
+ ## list.
+ return undef if ($#slist == -1);
+
+ ## Convert the array into keys for a hash table
+ my %shash;
+ @shash{@slist} = ();
+
+ ## Find out which source files are listed
+ my $names = $self->{$dest};
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$$names{$name}}) {
+ my $array = $$names{$name}->{$key};
+ my $count = scalar(@$array);
+ for(my $i = 0; $i < $count; ++$i) {
+ ## Is the source file in the component array?
+ if (exists $shash{$$array[$i]}) {
+ ## Remove the element and fix the index and count
+ splice(@$array, $i, 1);
+ --$count;
+ --$i;
+ }
+ }
+ }
+ }
+}
+
+
+sub generated_source_listed {
+ my($self, $gent, $tag, $arr, $sext) = @_;
+ my $names = $self->{$tag};
+
+ ## Find out which generated source files are listed
+ foreach my $name (keys %$names) {
+ my $comps = $$names{$name};
+ foreach my $key (keys %$comps) {
+ foreach my $val (@{$$comps{$key}}) {
+ foreach my $i (keys %$arr) {
+ ## If $gent doesn't cause $tag files to be generated, then we
+ ## can just return a non-zero value to short-circuit attempting
+ ## to add generated files after the caller continues.
+ my @gfiles = $self->generated_filenames($$arr{$i}, $gent, $tag, $i);
+ return 2 if ($#gfiles == -1);
+
+ foreach my $re (@gfiles) {
+ $re = $self->escape_regex_special($re);
+ return 1 if ($val =~ /$re$/);
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+sub list_default_generated {
+ my($self, $gentype, $tags) = @_;
+
+ ## This method is called when the user has custom input files and has
+ ## provided source files. If the user defaults the component (i.e.
+ ## source_files, resource_files, etc.) they are filled in by the
+ ## generate_default_components method.
+
+ if (defined $self->{'generated_exts'}->{$gentype} &&
+ $self->{'generated_exts'}->{$gentype}->{'automatic_out'}) {
+ ## After all source and headers have been defaulted, see if we
+ ## need to add the generated files
+ if (defined $self->{$gentype}) {
+ ## Build up the list of files
+ my %arr;
+ my $names = $self->{$gentype};
+ my $group;
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$$names{$name}}) {
+ my $array = $$names{$name}->{$key};
+
+ ## Take the last group name we encounter
+ $group = $key if ($key ne $defgroup);
+
+ foreach my $val (@$array) {
+ $arr{$val} = $self->remove_wanted_extension(
+ $val,
+ $self->{'valid_components'}->{$gentype});
+ }
+ }
+ }
+
+ foreach my $type (@$tags) {
+ ## Do not add generated files if they are "special"
+ ## unless they haven't been explicitly supplied.
+ if ($gentype ne $type &&
+ (!$specialComponents{$type} ||
+ (!$self->{'special_supplied'}->{$type} ||
+ UNIVERSAL::isa($self->{'special_supplied'}->{$type}, 'ARRAY')))) {
+ if (!$self->generated_source_listed(
+ $gentype, $type, \%arr,
+ $self->{'valid_components'}->{$gentype})) {
+ $self->add_generated_files($gentype, $type, $group, \%arr);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+sub prepend_gendir {
+ my($self, $created, $ofile, $gentype) = @_;
+ my $key;
+
+ if (defined $self->{'flag_overrides'}->{$gentype}) {
+ foreach my $ext (@{$self->{'valid_components'}->{$gentype}}) {
+ my $e = $ext;
+ $e =~ s/\\//g;
+ $key = "$ofile$e";
+
+ last if (defined $self->{'flag_overrides'}->{$gentype}->{$key});
+ $key = undef;
+ }
+
+ if (defined $key) {
+ if (StringProcessor::fgrep('gendir',
+ $self->{'matching_assignments'}->{$gentype})) {
+ my $dir = $self->{'flag_overrides'}->{$gentype}->{$key}->{'gendir'};
+ if (defined $dir) {
+ ## Convert the file to unix style for basename
+ if ($self->{'convert_slashes'}) {
+ $created =~ s/\\/\//g;
+ $dir =~ s/\\/\//g;
+ }
+ return ($dir eq '.' ? '' : "$dir/") . $self->mpc_basename($created);
+ }
+ }
+ }
+ }
+
+ return $created;
+}
+
+
+sub list_generated_file {
+ my($self, $gentype, $tag, $array, $file, $ofile) = @_;
+ my $count = 0;
+
+ ## Go through each file listed in our original type and attempt to find
+ ## out if it is the generated file we may need to add ($file).
+ foreach my $gen ($self->get_component_list($gentype, 1)) {
+ my $input = $gen;
+
+ ## Take the file and see if it contains an extension that our
+ ## generating type ($gentype) knows about. If it does, remove it and
+ ## stop looking for the extension.
+ foreach my $ext (@{$self->{'valid_components'}->{$gentype}}) {
+ ## Remove the extension.
+ ## If it works, then we can exit this loop.
+ last if ($gen =~ s/$ext$//);
+ }
+
+ ## If the user provided file does not match any of the
+ ## extensions specified by the custom definition, we need
+ ## to remove the extension or else this file will not be
+ ## added to the project.
+ $gen =~ s/\.[^\.]+$// if ($gen eq $input);
+
+ ## See if we need to add the file. We always need to check since the
+ ## output file may have absolutely nothing in common with the input
+ ## file.
+ foreach my $created ($self->generated_filenames($gen, $gentype,
+ $tag, $input)) {
+ ## $gen is a file that has a custom definition that generates
+ ## files of the type $tag. The $file passed in is of type
+ ## $gentype and, as far as I can tell, $created will always be
+ ## longer or of the same length of $file. It doesn't really
+ ## matter if $file contains a '.' or not.
+ if (index($created, $file) != -1) {
+ if (defined $ofile) {
+ $created = $self->prepend_gendir($created, $ofile, $gentype);
+ }
+ if (!$self->already_added($array, $created)) {
+ push(@$array, $created);
+ ++$count;
+ }
+ last;
+ }
+ }
+ }
+
+ return $count;
+}
+
+
+sub add_corresponding_component_files {
+ my($self, $filecomp, $tag) = @_;
+ my $grname = $grouped_key . $tag;
+
+ ## Create a hash array keyed off of the existing files of the type
+ ## that we plan on adding.
+ my $fexist = 0;
+ my %scfiles;
+ my $names = $self->{$tag};
+ foreach my $name (keys %$names) {
+ ## Check to see if files exist in the default group
+ if (defined $$names{$name}->{$defgroup} &&
+ defined $$names{$name}->{$defgroup}->[0]) {
+ $fexist = 1;
+ }
+ foreach my $comp (keys %{$$names{$name}}) {
+ @scfiles{@{$$names{$name}->{$comp}}} = ();
+ }
+ }
+
+ ## Create an array of extensions for the files we want to add
+ my @exts;
+ foreach my $ext (@{$self->{'valid_components'}->{$tag}}) {
+ push(@exts, $ext);
+ $exts[$#exts] =~ s/\\//g;
+ }
+
+ ## Check each file against a possible new file addition
+ my $adddefaultgroup = 0;
+ my $oktoadddefault = 0;
+ foreach my $sfile (keys %$filecomp) {
+ my $found = 0;
+ foreach my $ext (@exts) {
+ if (exists $scfiles{"$sfile$ext"}) {
+ $found = 1;
+ last;
+ }
+ }
+
+ if (!$found) {
+ ## Get the array of files for the selected component name
+ my $array = [];
+ my $comp = $$filecomp{$sfile};
+ foreach my $name (keys %$names) {
+ if (defined $$names{$name}->{$comp}) {
+ $array = $$names{$name}->{$comp};
+ }
+ }
+
+ ## First, see if it will be generated so that we can correctly
+ ## deal with 'gendir' settings.
+ foreach my $gentype (keys %{$self->{'generated_exts'}}) {
+ $found += $self->list_generated_file($gentype, $tag, $array, $sfile);
+ }
+
+ ## Next check to see if the file exists
+ if (!$found) {
+ foreach my $ext (@exts) {
+ if (-r "$sfile$ext") {
+ my $file = "$sfile$ext";
+ if (!$self->already_added($array, $file)) {
+ push(@$array, $file);
+ ++$found;
+ }
+ last;
+ }
+ }
+ }
+
+ ## If we have any files at all in the component array, check
+ ## to see if we need to add a new group name
+ if (defined $$array[0]) {
+ if ($comp eq $defgroup) {
+ $adddefaultgroup = 1;
+ }
+ else {
+ my $grval = $self->get_assignment($grname);
+ if (!defined $grval ||
+ !StringProcessor::fgrep($comp, $self->create_array($grval))) {
+ $self->process_assignment_add($grname, $comp);
+ }
+ $oktoadddefault = 1;
+ $adddefaultgroup |= $fexist;
+ }
+
+ ## Put the array back into the component list
+ if ($found) {
+ foreach my $name (keys %$names) {
+ $$names{$name}->{$comp} = $array;
+ }
+ }
+ }
+ }
+ }
+
+ ## We only need to add the default group name if we wanted to
+ ## add the default group when adding new files and we added a group
+ ## by some other name. Otherwise, defaulted files would always be
+ ## in a group, which is not what we want.
+ if ($adddefaultgroup && $oktoadddefault) {
+ $self->process_assignment_add($grname, $defgroup);
+ }
+}
+
+
+sub get_default_project_name {
+ my $self = shift;
+ my $name = $self->{'current_input'};
+
+ if ($name eq '') {
+ $name = $self->transform_file_name($self->base_directory());
+ }
+ else {
+ ## Since files on UNIX can have back slashes, we transform them
+ ## into underscores.
+ $name =~ s/\\/_/g;
+
+ ## Convert the name to a usable name
+ $name = $self->transform_file_name($name);
+
+ ## Take off the extension
+ $name =~ s/\.[^\.]+$//;
+ }
+
+ return $name;
+}
+
+
+sub remove_excluded {
+ my $self = shift;
+ my @tags = @_;
+
+ ## Process each file type and remove the excluded files
+ foreach my $tag (@tags) {
+ my $names = $self->{$tag};
+ foreach my $name (keys %$names) {
+ foreach my $comp (keys %{$$names{$name}}) {
+ my $count = scalar(@{$$names{$name}->{$comp}});
+ for(my $i = 0; $i < $count; ++$i) {
+ my $file = $$names{$name}->{$comp}->[$i];
+ if (defined $self->{'remove_files'}->{$tag}->{$file}) {
+ splice(@{$$names{$name}->{$comp}}, $i, 1);
+ --$i;
+ --$count;
+ }
+ else {
+ ## The file does not match exactly with one of the files to
+ ## remove. Look for wildcard specifications in the files to
+ ## be removed and perform the removal if one of them matches
+ ## the current file.
+ foreach my $key (keys %{$self->{'remove_files'}->{$tag}}) {
+ if ($key =~ /[\*\?\[\]]/) {
+ my $regex = $key;
+ $regex =~ s/\./\\./g;
+ $regex =~ s/\*/\.\*/g;
+ $regex =~ s/\?/\./g;
+ if ($file =~ /^$regex$/) {
+ splice(@{$$names{$name}->{$comp}}, $i, 1);
+ --$i;
+ --$count;
+ last;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ delete $self->{'remove_files'}->{$tag};
+ }
+}
+
+
+sub sort_generated_types {
+ ## We need to sort the custom component types such that a custom type
+ ## that generates input for another custom type comes first in the
+ ## list.
+ my($self, $left, $right, $norecurse) = @_;
+ foreach my $key (keys %{$self->{'generated_exts'}->{$left}}) {
+ if ($key =~ /_files$/) {
+ foreach my $regex (@{$self->{'generated_exts'}->{$left}->{$key}}) {
+ my $ext = $regex;
+ $ext =~ s/\\//g;
+ foreach my $vreg (@{$self->{'valid_components'}->{$right}}) {
+ return -1 if ($ext =~ /$vreg$/);
+ }
+ }
+ }
+ }
+ if (!$norecurse && $self->sort_generated_types($right, $left, 1) == -1) {
+ return 1;
+ }
+
+ return 0;
+}
+
+sub generate_defaults {
+ my $self = shift;
+
+ ## Generate default project name
+ if (!defined $self->get_assignment('project_name')) {
+ $self->set_project_name($self->get_default_project_name());
+ }
+
+ ## Generate the default pch file names (if needed)
+ my @files = $self->generate_default_file_list(
+ '.', [],
+ undef, $self->get_assignment('recurse'));
+ $self->generate_default_pch_filenames(\@files);
+
+ ## If the pch file names are empty strings then we need to fix that
+ $self->fix_pch_filenames();
+
+ ## Generate default components, but %specialComponents
+ ## are skipped in the initial default components generation
+ $self->generate_default_components(\@files);
+
+ ## Remove source files that are also listed in the template files
+ ## If we do not do this, then generated projects can be invalid.
+ $self->remove_duplicated_files('source_files', 'template_files');
+
+ ## If pch files are listed in header_files or source_files more than
+ ## once, we need to remove the extras
+ $self->remove_extra_pch_listings();
+
+ ## Generate the default generated list of files only if we defaulted
+ ## the generated file list. I want to ensure that source_files comes
+ ## first in the list to pick up group information (since source_files
+ ## are most likely going to be grouped than anything else).
+ my @vc = sort { return -1 if $a eq 'source_files';
+ return 1 if $b eq 'source_files';
+ return $b cmp $a; } keys %{$self->{'valid_components'}};
+ my @gvc = sort { $self->sort_generated_types($a, $b)
+ } keys %{$self->{'generated_exts'}};
+ foreach my $gentype (@gvc) {
+ $self->list_default_generated($gentype, \@vc);
+ }
+
+ ## Now that all of the source files have been added
+ ## we need to remove those that have need to be removed
+ $self->remove_excluded('source_files');
+
+ ## Collect up all of the source files that have already been listed
+ ## with the extension removed for use directly below.
+ my %sourcecomp;
+ foreach my $sourcetag (keys %sourceComponents) {
+ my $names = $self->{$sourcetag};
+ foreach my $name (keys %$names) {
+ foreach my $comp (keys %{$$names{$name}}) {
+ foreach my $sfile (@{$$names{$name}->{$comp}}) {
+ my $mod = $sfile;
+ $mod =~ s/\.[^\.]+$//;
+ $sourcecomp{$mod} = $comp;
+ }
+ }
+ }
+ }
+
+ ## Add %specialComponents files based on the
+ ## source_components (i.e. .h and .i or .inl based on .cpp)
+ foreach my $tag (keys %specialComponents) {
+ $self->add_corresponding_component_files(\%sourcecomp, $tag);
+ }
+
+ ## Now, if the %specialComponents are still empty
+ ## then take any file that matches the components extension
+ foreach my $tag (keys %specialComponents) {
+ if (!$self->{'special_supplied'}->{$tag} ||
+ UNIVERSAL::isa($self->{'special_supplied'}->{$tag}, 'ARRAY')) {
+ my $names = $self->{$tag};
+ if (defined $names) {
+ ## We only want to generate default components if we have
+ ## defaulted the source files or we have no files listed
+ ## in the current special component.
+ my $ok = $self->{'defaulted'}->{'source_files'};
+ if (!$ok) {
+ my @all;
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$$names{$name}}) {
+ push(@all, @{$$names{$name}->{$key}});
+ }
+ }
+ $ok = (!defined $all[0]);
+ }
+ if ($ok) {
+ ## If the "special" type was supplied and it was all
+ ## directories, we need to use those directories to generate
+ ## the default components instead of the current directory.
+ my $fileref = \@files;
+ if (defined $self->{'special_supplied'}->{$tag} &&
+ UNIVERSAL::isa($self->{'special_supplied'}->{$tag}, 'ARRAY')) {
+ my @special;
+ foreach my $dir (@{$self->{'special_supplied'}->{$tag}}) {
+ push(@special, $self->generate_default_file_list(
+ $dir, [], undef,
+ $self->get_assignment('recurse')));
+ }
+ $fileref = \@special;
+ }
+ $self->generate_default_components($fileref, $tag);
+ }
+ }
+ }
+ }
+
+ ## Now that all of the other files have been added
+ ## we need to remove those that have need to be removed
+ my @rmkeys = keys %{$self->{'remove_files'}};
+ $self->remove_excluded(@rmkeys) if (defined $rmkeys[0]);
+
+ ## Tie custom files together if need be. This currently only applies
+ ## to types with command helpers. At some point, if it is found to be
+ ## desirous, we could extend the MPC syntax somehow to support this
+ ## sort of thing manually.
+ my $dep = 'dependent';
+ foreach my $gentype (@gvc) {
+ my $cmdHelper = CommandHelper::get($gentype);
+ if (defined $cmdHelper) {
+ ## There has to be at least two files files in order for
+ ## something to be tied together.
+ my @files = $self->get_component_list($gentype, 1);
+ if ($#files >= 1) {
+ foreach my $file (@files) {
+ my $part = $self->remove_wanted_extension(
+ $file, $self->{'valid_components'}->{$gentype});
+ my($tied, $vc) = $cmdHelper->get_tied($file, \@files);
+ foreach my $tie (@$tied) {
+ my @gen;
+ if (!defined $vc) {
+ foreach $vc (@vc) {
+ @gen = $self->generated_filenames($part, $gentype,
+ $vc, $file);
+ last if ($#gen >= 0);
+ }
+ }
+
+ ## We have a tied file, now we need to actually perform
+ ## the tieing of the two. We will do this by saying that
+ ## the output of the original is necessary for the
+ ## processing of the tied file.
+ @gen = $self->generated_filenames($part, $gentype,
+ $vc, $file) if (!$gen[0]);
+
+ ## We have found a set of files that are generated
+ ## based on the component type of the original file
+ ## ($gentype), so we just add the first one and
+ ## we're done.
+ my $first = $gen[0];
+ $self->{'flag_overrides'}->{$gentype}->{$tie}->{$dep} =
+ $self->{'generated_exts'}->{$gentype}->{$dep}
+ if (!defined $self->{'flag_overrides'}->{$gentype}->{$tie}->{$dep});
+
+ $self->{'flag_overrides'}->{$gentype}->{$tie}->{$dep} .= " $first"
+ if (!defined $self->{'flag_overrides'}->{$gentype}->{$tie}->{$dep} ||
+ $self->{'flag_overrides'}->{$gentype}->{$tie}->{$dep} !~ /\b$first\b/);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+sub set_project_name {
+ my($self, $name) = @_;
+
+ ## Save the unmodified project name so that when we
+ ## need to determine the default target name, we can use
+ ## what is expected by the user.
+ $self->{'unmodified_project_name'} = $name;
+
+ ## If we are applying the name modifier to the project
+ ## then we will modify the project name
+ if ($self->get_apply_project()) {
+ my $nmod = $self->get_name_modifier();
+
+ if (defined $nmod) {
+ $nmod =~ s/\*/$name/g;
+ $name = $nmod;
+ }
+ }
+
+ ## Set the project_name assignment so that the TemplateParser
+ ## can get the project name.
+ $self->process_assignment('project_name', $name);
+}
+
+
+sub project_name {
+ return $_[0]->get_assignment('project_name');
+}
+
+
+sub lib_target {
+ my $self = shift;
+ return (defined $self->get_assignment('sharedname') ||
+ defined $self->get_assignment('staticname'));
+}
+
+
+sub exe_target {
+ return (defined $_[0]->get_assignment('exename'));
+}
+
+
+sub get_component_list {
+ my($self, $tag, $noconvert) = @_;
+ my $names = $self->{$tag};
+ my @list;
+
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$$names{$name}}) {
+ push(@list, @{$$names{$name}->{$key}});
+ }
+ }
+
+ ## By default, if 'convert_slashes' is true, then we convert slashes
+ ## to backslashes. There are cases where we do not want to convert
+ ## the slashes, in that case get_component_list() was called with
+ ## an additional parameter indicating this.
+ if (!$noconvert && $self->{'convert_slashes'}) {
+ foreach my $item (@list) {
+ $item =~ s/\//\\/g;
+ }
+ }
+
+ if ($self->{'sort_files'}) {
+ @list = sort { $self->file_sorter($a, $b) } @list;
+ }
+
+ return @list;
+}
+
+
+sub check_custom_output {
+ my($self, $based, $cinput, $ainput, $type, $comps) = @_;
+ my @outputs;
+
+ foreach my $array ($self->generated_filenames($cinput, $based,
+ $type, $ainput, 0, 1)) {
+ foreach my $built (@$array) {
+ if (@$comps == 0) {
+ push(@outputs, $built);
+ last;
+ }
+ elsif (defined $specialComponents{$type} &&
+ (!$self->{'special_supplied'}->{$type} ||
+ UNIVERSAL::isa($self->{'special_supplied'}->{$type}, 'ARRAY'))) {
+ push(@outputs, $built);
+ last;
+ }
+ else {
+ my $base = $built;
+ $base =~ s/\\/\//g if ($self->{'convert_slashes'});
+ my $re = $self->escape_regex_special($self->mpc_basename($base));
+ foreach my $c (@$comps) {
+ ## We only match if the built file name matches from
+ ## beginning to end or from a slash to the end.
+ if ($c =~ /^$re$/ || $c =~ /[\/\\]$re$/) {
+ push(@outputs, $built);
+ last;
+ }
+ }
+ }
+ }
+ }
+
+ return @outputs;
+}
+
+
+sub get_special_value {
+ my $self = shift;
+ my $type = shift;
+ my $cmd = shift;
+ my $based = shift;
+ my @params = @_;
+
+ ## These names (held in $type) are variables that contain various
+ ## commands that will be used in templates within the context of a
+ ## foreach (e.g., <%custom_type->input_files%> or <%feature->value%>).
+ if ($type eq 'feature') {
+ return $self->get_feature_value($cmd, $based);
+ }
+ elsif (index($type, 'custom_type') == 0) {
+ return $self->get_custom_value($cmd, $based, @params);
+ }
+ elsif (index($type, $grouped_key) == 0) {
+ return $self->get_grouped_value($type, $cmd, $based);
+ }
+
+ return undef;
+}
+
+
+sub get_feature_value {
+ my($self, $cmd, $based) = @_;
+
+ if ($cmd eq 'value') {
+ my $val = $self->{'feature_parser'}->get_value($based);
+ if (defined $val && $val != 0) {
+ return 1;
+ }
+ }
+
+ return undef;
+}
+
+
+sub get_grouped_value {
+ my($self, $type, $cmd, $based) = @_;
+ my $value;
+
+ ## Make it all lower case
+ $type = lc($type);
+
+ ## Remove the grouped_ part
+ $type =~ s/^$grouped_key//;
+
+ ## Add the s if it isn't there
+ $type .= 's' if ($type !~ /s$/);
+
+ my $names = $self->{$type};
+ if ($cmd eq 'files') {
+ foreach my $name (keys %$names) {
+ my $comps = $$names{$name};
+ my @keys = keys %$comps;
+ if (StringProcessor::fgrep($based, \@keys)) {
+ if ($self->{'convert_slashes'}) {
+ my @converted;
+ foreach my $file (@{$$comps{$based}}) {
+ push(@converted, $self->slash_to_backslash($file));
+ }
+ $value = \@converted;
+ }
+ else {
+ $value = $$comps{$based};
+ }
+ if ($self->{'sort_files'}) {
+ my @sorted = sort { $self->file_sorter($a, $b) } @$value;
+ $value = \@sorted;
+ }
+ }
+ }
+ }
+ elsif ($cmd eq 'component_name') {
+ ## If there is more than one name, then we will need
+ ## to deal with that at a later time.
+ foreach my $name (keys %$names) {
+ $value = $name;
+ }
+ }
+
+ return $value;
+}
+
+
+sub get_command_subs {
+ my $self = shift;
+ my %valid;
+
+ ## Add the built-in OS compatibility commands
+ if (UNIVERSAL::isa($self, 'WinProjectBase') ||
+ $self->use_win_compatibility_commands()) {
+ $valid{'cat'} = 'type';
+ $valid{'cmp'} = 'fc';
+ $valid{'cp'} = 'copy /y';
+ $valid{'mkdir'} = 'mkdir';
+ $valid{'mv'} = 'move /y';
+ $valid{'os'} = 'win32';
+ $valid{'rm'} = 'del /f/s/q';
+ $valid{'rmdir'} = 'rmdir /s/q';
+ $valid{'nul'} = 'nul';
+ $valid{'slash'} = '\\';
+ $valid{'bat'} = '.bat';
+ $valid{'cmd'} = '.cmd';
+ $valid{'exe'} = '.exe';
+ }
+ else {
+ $valid{'cat'} = 'cat';
+ $valid{'cmp'} = 'cmp';
+ $valid{'cp'} = 'cp -f';
+ $valid{'mkdir'} = 'mkdir -p';
+ $valid{'mv'} = 'mv -f';
+ $valid{'os'} = 'unix';
+ $valid{'rm'} = 'rm -rf';
+ $valid{'rmdir'} = 'rm -rf';
+ $valid{'nul'} = '/dev/null';
+ $valid{'slash'} = '/';
+ $valid{'bat'} = '';
+ $valid{'cmd'} = '';
+ $valid{'exe'} = '';
+ }
+
+ ## Add the project specific compatibility commands
+ $valid{'gt'} = $self->get_gt_symbol();
+ $valid{'lt'} = $self->get_lt_symbol();
+ $valid{'and'} = $self->get_and_symbol();
+ $valid{'or'} = $self->get_or_symbol();
+ $valid{'quote'} = $self->get_quote_symbol();
+ $valid{'equote'} = $self->get_escaped_quote_symbol();
+ $valid{'crlf'} = $self->crlf();
+ $valid{'cmdsep'} = $self->get_cmdsep_symbol();
+ $valid{'temporary'} = 'temp.$$$$.' . int(rand(0xffffffff));
+ $valid{'prj_type'} = $self->{'pctype'};
+
+ return \%valid;
+}
+
+
+sub replace_parameters {
+ my($self, $str, $valid, $nowarn, $input, $output, $always_clear) = @_;
+
+ my %saved;
+ my $count = 0;
+ while ($str =~ /<%(\w+)(\(\w+\))?%>/) {
+ my $name = $1;
+ my $modifier = $2;
+ if (defined $modifier) {
+ my $tmp = $name;
+ $name = $modifier;
+ $name =~ s/[\(\)]//g;
+ $modifier = $tmp;
+ }
+
+ ## Support both pseudo variables and project settings
+ if (defined $$valid{$name} || $self->is_keyword($name)) {
+ ## If the pseudo variable is defined or the project setting has a
+ ## value, then we'll need to do the replacement. However, if it's
+ ## a project keyword and it's not defined, we will need to delay
+ ## the replacement until later (unless $always_clear is true).
+ my $replace;
+ my $clear = $always_clear;
+ if (defined $$valid{$name}) {
+ $replace = $$valid{$name};
+ }
+ elsif ($self->is_keyword($name)) {
+ $replace = $self->get_assignment($name);
+ }
+
+ ## Perform the modification and replacement here
+ if (defined $replace) {
+ if (defined $modifier) {
+ if ($modifier eq 'noextension') {
+ $replace =~ s/\.[^\.]+$//;
+ }
+ else {
+ $self->warning("Unknown parameter modifier $modifier.");
+ }
+ }
+ $str =~ s/<%\w+(\(\w+\))?%>/$replace/;
+ }
+ elsif ($clear) {
+ ## We need to clear out this variable usage.
+ $str =~ s/<%\w+(\(\w+\))?%>//;
+ }
+ else {
+ ## Save this variable usage to be put back after we're done
+ ## processing the string.
+ my $key = "\1" . $count++ . "\1";
+ if ($str =~ s/(<%\w+(\(\w+\))?%>)/$key/) {
+ $saved{$key} = $1;
+ }
+ }
+ }
+ else {
+ $str =~ s/<%\w+(\(\w+\))?%>//;
+
+ ## We only want to warn the user that we did not recognize the
+ ## pseudo template parameter if there was an input and an output
+ ## file passed to this function. If this variable was used
+ ## without the parenthesis (as in an if statement), then we don't
+ ## want to warn the user.
+ if (defined $input && defined $output) {
+ if (!defined $$nowarn{$name}) {
+ $self->warning("<%$name%> was not recognized.");
+ }
+
+ ## If we didn't recognize the pseudo template parameter then
+ ## we don't want to return anything back.
+ return undef;
+ }
+ }
+ }
+
+ ## Replace the saved variables so that they may be replaced (or
+ ## removed) later on.
+ foreach my $key (keys %saved) {
+ $str =~ s/$key/$saved{$key}/;
+ }
+ return $str;
+}
+
+
+sub convert_command_parameters {
+ my($self, $ktype, $str, $input, $output) = @_;
+ my %nowarn;
+ my %valid = %{$self->{'command_subs'}};
+
+ ## Add in the values that change for every call to this function
+ $valid{'temporary'} = 'temp.$$$$.' . int(rand(0xffffffff));
+
+ if (defined $input) {
+ $valid{'input'} = $input;
+ $valid{'input_basename'} = $self->mpc_basename($input);
+ $valid{'input_dirname'} = $self->mpc_dirname($input);
+ $valid{'input_noext'} = $input;
+
+ ## An input file doesn't always have an extension. If there isn't
+ ## one, then we need to set the 'input_ext' field to an empty string
+ ## ($1 will not necessarily have a valid value).
+ if ($valid{'input_noext'} =~ s/(\.[^\.]+)$//) {
+ $valid{'input_ext'} = $1;
+ }
+ else {
+ $valid{'input_ext'} = '';
+ }
+
+ ## Check for the gendir setting associated with this input file. We
+ ## have to check at so many levels so we don't inadvertantly create
+ ## intermediate hash tables.
+ if (defined $self->{'flag_overrides'}->{$ktype} &&
+ defined $self->{'flag_overrides'}->{$ktype}->{$input} &&
+ $self->{'flag_overrides'}->{$ktype}->{$input}->{'gendir'}) {
+ $valid{'gendir'} = $self->{'flag_overrides'}->{$ktype}->{$input}->{'gendir'};
+ }
+ }
+
+ ## If there is no gendir setting, just set it to the current directory.
+ $valid{'gendir'} = '.' if (!defined $valid{'gendir'});
+
+ if (defined $output) {
+ my $first = 1;
+ $valid{'output'} = "@$output";
+ foreach my $out (@$output) {
+ ## An output file doesn't always have an extension. If there isn't
+ ## one, then we need to set the 'output_ext' field to an empty
+ ## string ($1 will not necessarily have a valid value).
+ my $noext = $out;
+ if ($noext =~ s/(\.[^\.]+)$//) {
+ $valid{'output_ext'} = $1;
+ }
+ else {
+ $valid{'output_ext'} = '';
+ }
+ $valid{'output_noext'} .= (!$first ? ' ' : '') . $noext;
+
+ ## In order to call basename or dirname, we must make sure that the
+ ## directory separators are forward slashes.
+ my $file = $out;
+ $file =~ s/\\/\//g if ($self->{'convert_slashes'});
+ $valid{'output_basename'} .= (!$first ? ' ' : '') .
+ $self->mpc_basename($file);
+ $valid{'output_dirname'} .= (!$first ? ' ' : '') .
+ $self->mpc_dirname($file);
+ $first = 0;
+ }
+ }
+
+ ## Add in the specific types of output files
+ if (defined $output) {
+ foreach my $type (keys %{$self->{'valid_components'}}) {
+ my $key = $type;
+ $key =~ s/s$//gi;
+ $nowarn{$key} = 1;
+ $nowarn{$key . '_noext'} = 1;
+ foreach my $ext (@{$self->{'valid_components'}->{$type}}) {
+ foreach my $out (@$output) {
+ if ($out =~ /$ext$/) {
+ $valid{$key} = $out;
+ $valid{$key . '_noext'} = $out;
+ $valid{$key . '_noext'} =~ s/$ext$//;
+ last;
+ }
+ }
+ }
+ }
+ }
+
+ return $self->replace_parameters($str, \%valid, \%nowarn, $input, $output, 1);
+}
+
+
+sub get_custom_value {
+ my $self = shift;
+ my $cmd = shift;
+ my $based = shift;
+ my @params = @_;
+ my $value;
+
+ if ($cmd eq 'input_files') {
+ ## Get the component list for the component type
+ my @array = $self->get_component_list($based);
+
+ ## Check for directories in the component list. If the component
+ ## type is not automatic, we may have directories here and will need
+ ## to get the file list for that type.
+ my $once;
+ for(my $i = 0; $i < scalar(@array); ++$i) {
+ if (-d $array[$i]) {
+ if (!defined $once) {
+ $once = {'recurse' => $self->get_assignment('recurse'),
+ 'pchh' => $self->get_assignment('pch_header'),
+ 'pchc' => $self->get_assignment('pch_source'),
+ };
+ }
+ my @built;
+ $self->sift_default_file_list($based, $array[$i], \@built,
+ $self->{'valid_components'}->{$based},
+ $$once{'recurse'},
+ $$once{'pchh'}, $$once{'pchc'});
+ splice(@array, $i, 1, @built);
+ $i += $#built;
+ }
+ }
+
+ $value = \@array;
+
+ $self->{'custom_output_files'} = {};
+ my %vcomps;
+ foreach my $vc (keys %{$self->{'valid_components'}}) {
+ my @comps = $self->get_component_list($vc);
+ $vcomps{$vc} = \@comps;
+ }
+ $vcomps{$generic_key} = [];
+
+ foreach my $input (@array) {
+ my @outputs;
+ my $ainput = $input;
+ my $cinput = $input;
+
+ ## Remove the extension
+ $cinput =~ s/\.[^\.]+$//;
+
+ ## If we are converting slashes,
+ ## change them back for this parameter
+ $ainput =~ s/\\/\//g if ($self->{'convert_slashes'});
+
+ ## Add all of the output files. We can not add $generic_key to the
+ ## list here (as it used to be). It may have been handled by
+ ## generated_filenames.
+ foreach my $vc (keys %{$self->{'valid_components'}}) {
+ ## The output of multiple components could be input for the
+ ## current component type ($based). We need to avoid adding
+ ## duplicates here.
+ foreach my $file ($self->check_custom_output(
+ $based, $cinput, $ainput, $vc, $vcomps{$vc})) {
+ push(@outputs, $file) if (!StringProcessor::fgrep($file, \@outputs));
+ }
+ }
+ foreach my $file ($self->check_custom_output($based, $cinput,
+ $ainput, $generic_key,
+ $vcomps{$generic_key})) {
+ push(@outputs, $file) if (!StringProcessor::fgrep($file, \@outputs));
+ }
+
+ ## Add specially listed files avoiding duplicates. We don't want
+ ## to add these files if gendir is set to something besides .
+ if (defined $self->{'custom_special_output'}->{$based} &&
+ defined $self->{'custom_special_output'}->{$based}->{$ainput} &&
+ (!defined $self->{'flag_overrides'}->{$based} ||
+ !defined $self->{'flag_overrides'}->{$based}->{$ainput} ||
+ !defined $self->{'flag_overrides'}->{$based}->{$ainput}->{'gendir'} ||
+ $self->{'flag_overrides'}->{$based}->{$ainput}->{'gendir'} eq '.')) {
+ foreach my $file (@{$self->{'custom_special_output'}->{$based}->{$ainput}}) {
+ push(@outputs, $file) if (!StringProcessor::fgrep($file, \@outputs));
+ }
+ }
+
+ if ($self->{'convert_slashes'}) {
+ foreach my $output (@outputs) {
+ $output =~ s/\//\\/g;
+ }
+ }
+ if ($self->{'sort_files'}) {
+ @outputs = sort { $self->file_sorter($a, $b) } @outputs;
+ }
+ $self->{'custom_output_files'}->{$input} = \@outputs;
+ }
+ }
+ elsif ($cmd eq 'output_files') {
+ # Generate output files based on $based
+ if (defined $self->{'custom_output_files'}) {
+ $value = $self->{'custom_output_files'}->{$based};
+ }
+ }
+ elsif ($cmd eq 'source_output_files') {
+ # Generate source output files based on $based
+ if (defined $self->{'custom_output_files'}) {
+ $value = [];
+ foreach my $file (@{$self->{'custom_output_files'}->{$based}}) {
+ foreach my $ext (@{$self->{'valid_components'}->{'source_files'}}) {
+ if ($file =~ /$ext$/) {
+ ## We've found a file that matches one of the source file
+ ## extensions. Now we have to make sure that it doesn't
+ ## match a template file extension.
+ my $matched = 0;
+ foreach my $text (@{$self->{'valid_components'}->{'template_files'}}) {
+ if ($file =~ /$text$/) {
+ $matched = 1;
+ last;
+ }
+ }
+ push(@$value, $file) if (!$matched);
+ last;
+ }
+ }
+ }
+ }
+ }
+ elsif ($cmd eq 'non_source_output_files') {
+ # Generate non source output files based on $based
+ if (defined $self->{'custom_output_files'}) {
+ $value = [];
+ foreach my $file (@{$self->{'custom_output_files'}->{$based}}) {
+ my $source = 0;
+ foreach my $ext (@{$self->{'valid_components'}->{'source_files'}}) {
+ if ($file =~ /$ext$/) {
+ $source = 1;
+ ## We've found a file that matches one of the source file
+ ## extensions. Now we have to make sure that it doesn't
+ ## match a template file extension.
+ foreach my $text (@{$self->{'valid_components'}->{'template_files'}}) {
+ if ($file =~ /$text$/) {
+ $source = 0;
+ last;
+ }
+ }
+ last if ($source);
+ }
+ }
+ push(@$value, $file) if (!$source);
+ }
+ }
+ }
+ elsif ($cmd eq 'inputexts') {
+ my @array = @{$self->{'valid_components'}->{$based}};
+ foreach my $val (@array) {
+ $val =~ s/\\\.//g;
+ }
+ $value = \@array;
+ }
+ elsif ($cmd eq 'dependencies') {
+ ## If we are converting slashes, change them back for this parameter
+ $based =~ s/\\/\//g if ($self->{'convert_slashes'});
+ $value = $self->{'custom_special_depend'}->{$based};
+ }
+ elsif (defined $customDefined{$cmd}) {
+ $value = $self->get_assignment($cmd,
+ $self->{'generated_exts'}->{$based});
+ if (defined $value && ($customDefined{$cmd} & 0x14) != 0) {
+ $value = $self->convert_command_parameters($based, $value, @params);
+ }
+ }
+
+ return $value;
+}
+
+
+sub check_features {
+ my($self, $requires, $avoids, $info) = @_;
+ my $status = 1;
+ my $why;
+
+ if (defined $requires) {
+ foreach my $require (split(/\s+/, $requires)) {
+ my $fval = $self->{'feature_parser'}->get_value($require);
+
+ ## By default, if the feature is not listed, then it is enabled.
+ if (defined $fval && !$fval) {
+ $why = "requires $require";
+ $status = 0;
+ last;
+ }
+
+ ## For automakes sake, if we're to this point the feature is
+ ## enabled and we will set it in the feature parser explicitly
+ if (!defined $fval) {
+ $self->{'feature_parser'}->parse_line(undef, "$require = 1");
+ }
+ }
+ }
+
+ ## If it passes the requires, then check the avoids
+ if ($status) {
+ if (defined $avoids) {
+ foreach my $avoid (split(/\s+/, $avoids)) {
+ my $fval = $self->{'feature_parser'}->get_value($avoid);
+
+ ## By default, if the feature is not listed, then it is enabled.
+ if (!defined $fval || $fval) {
+ $why = "avoids $avoid";
+ $status = 0;
+ last;
+ }
+ }
+ }
+ }
+
+ if ($info && !$status) {
+ $self->details("Skipping " . $self->get_assignment('project_name') .
+ " ($self->{'current_input'}), it $why.");
+ }
+
+ return $status;
+}
+
+
+sub need_to_write_project {
+ my $self = shift;
+ my $count = 0;
+
+ ## We always write a project if the user has provided a verbatim.
+ ## We have no idea what that verbatim clause does, so we need to just
+ ## do what the user tells us to do.
+ return 1 if (defined $self->{'verbatim'}->{$self->{'pctype'}});
+
+ ## The order here is important, we must check for source or resource
+ ## files first and then for custom input files.
+ foreach my $key ('source_files', $self->get_resource_tag(),
+ keys %{$self->{'generated_exts'}}) {
+ my $names = $self->{$key};
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$names->{$name}}) {
+ ## See if the project contains a file that corresponds to this
+ ## component name.
+ if (defined $names->{$name}->{$key}->[0]) {
+ if ($count >= 2) {
+ ## Return 2 if we have found a custom input file (and thus no
+ ## source or resource files due to the foreach order).
+ return 2;
+ }
+ ## We have either source files or resource files, we need to
+ ## see if this project creator supports the current language.
+ ## If it doesn't then we don't need to create the project.
+ elsif ($self->languageSupported()) {
+ ## Return 1 if we have found a source file or a resource file.
+ return 1;
+ }
+ }
+ }
+ }
+ $count++;
+ }
+
+ ## Indicate that there is no need to write the project
+ return 0;
+}
+
+
+sub write_output_file {
+ my($self, $webapp) = @_;
+ my $status = 0;
+ my $error;
+ my $tover = $self->get_template_override();
+ my @templates = $self->get_template();
+
+ ## The template override will override all templates
+ @templates = ($tover) if (defined $tover);
+
+ foreach my $template (@templates) {
+ ## Save the template name for use as a key for various function calls
+ $self->{'current_template'} = $template;
+
+ ## Create the output file name based on the project name and the
+ ## template that we're currently using.
+ my $name = $self->transform_file_name(
+ $self->project_file_name(undef,
+ $self->{'current_template'}));
+
+ ## If the template files does not end in the template extension
+ ## then we will add it on.
+ if ($template !~ /$TemplateExtension$/) {
+ $template .= '.' . $TemplateExtension;
+ }
+
+ ## If the template file does not contain a path, then we
+ ## will search through the include paths for it.
+ my $tfile;
+ if ($template =~ /[\/\\]/i) {
+ $tfile = $template;
+ }
+ else {
+ $tfile = $self->search_include_path($template);
+ }
+
+ if (defined $tfile) {
+ ## Read in the template values for the specific target and project
+ ## type. The template input file we get may depend upon the
+ ## current template that we're using.
+ ($status, $error) = $self->read_template_input(
+ $self->{'current_template'});
+ last if (!$status);
+
+ my $tp = new TemplateParser($self);
+
+ ## Set the project_file assignment for the template parser
+ $self->process_assignment('project_file', $name);
+
+ ($status, $error) = $tp->parse_file($tfile);
+ last if (!$status);
+
+ if (defined $self->{'source_callback'}) {
+ my $cb = $self->{'source_callback'};
+ my $pjname = $self->get_assignment('project_name');
+ my @list = $self->get_component_list('source_files');
+ if (UNIVERSAL::isa($cb, 'ARRAY')) {
+ my @copy = @$cb;
+ my $s = shift(@copy);
+ &$s(@copy, $name, $pjname, \@list);
+ }
+ elsif (UNIVERSAL::isa($cb, 'CODE')) {
+ &$cb($name, $pjname, \@list);
+ }
+ else {
+ $self->warning("Ignoring callback: $cb.");
+ }
+ }
+
+ if ($self->get_toplevel()) {
+ my $outdir = $self->get_outdir();
+ my $oname = $name;
+
+ $name = "$outdir/$name";
+
+ my $fh = new FileHandle();
+ my $dir = $self->mpc_dirname($name);
+
+ mkpath($dir, 0, 0777) if ($dir ne '.');
+
+ if ($webapp) {
+ ## At this point in time, webapps do not get a project file,
+ ## but they do appear in the workspace
+ }
+ elsif ($self->compare_output()) {
+ ## First write the output to a temporary file
+ my $tmp = "$outdir/MPC$>.$$";
+ my $different = 1;
+ if (open($fh, ">$tmp")) {
+ my $lines = $tp->get_lines();
+ foreach my $line (@$lines) {
+ print $fh $line;
+ }
+ close($fh);
+
+ $different = 0 if (!$self->files_are_different($name, $tmp));
+ }
+ else {
+ $error = "Unable to open $tmp for output.";
+ $status = 0;
+ last;
+ }
+
+ ## If they are different, then rename the temporary file
+ if ($different) {
+ unlink($name);
+ if (rename($tmp, $name)) {
+ $self->post_file_creation($name);
+ }
+ else {
+ $error = "Unable to open $name for output.";
+ $status = 0;
+ last;
+ }
+ }
+ else {
+ ## We will pretend that we wrote the file
+ unlink($tmp);
+ }
+ }
+ else {
+ if (open($fh, ">$name")) {
+ my $lines = $tp->get_lines();
+ foreach my $line (@$lines) {
+ print $fh $line;
+ }
+ close($fh);
+ $self->post_file_creation($name);
+ }
+ else {
+ $error = "Unable to open $name for output.";
+ $status = 0;
+ last;
+ }
+ }
+
+ ## There may be more than one template associated with this
+ ## project creator. If there is, we can only add one generated
+ ## file and we rely on the project creator to tell us which
+ ## template generates the file that we need to track.
+ $self->add_file_written($oname)
+ if ($self->file_visible($self->{'current_template'}));
+ }
+ }
+ else {
+ $error = "Unable to locate the template file: $template.";
+ $status = 0;
+ last;
+ }
+ }
+ return $status, $error;
+}
+
+
+sub write_install_file {
+ my $self = shift;
+ my $fh = new FileHandle();
+ my $insfile = $self->transform_file_name(
+ $self->get_assignment('project_name')) .
+ '.ins';
+ my $outdir = $self->get_outdir();
+
+ $insfile = "$outdir/$insfile";
+
+ unlink($insfile);
+ if (open($fh, ">$insfile")) {
+ foreach my $vc (keys %{$self->{'valid_components'}}) {
+ my $names = $self->{$vc};
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$$names{$name}}) {
+ my $array = $$names{$name}->{$key};
+ if (defined $$array[0]) {
+ print $fh "$vc:\n";
+ foreach my $file (@$array) {
+ print $fh "$file\n";
+ }
+ print $fh "\n";
+ }
+ }
+ }
+ }
+ if ($self->exe_target()) {
+ my $exeout = $self->get_assignment('exeout');
+ print $fh "exe_output:\n",
+ (defined $exeout ? $self->relative($exeout) : ''),
+ ' ', $self->get_assignment('exename'), "\n";
+ }
+ elsif ($self->lib_target()) {
+ my $shared = $self->get_assignment('sharedname');
+ my $static = $self->get_assignment('staticname');
+ my $dllout = $self->relative($self->get_assignment('dllout'));
+ my $libout = $self->relative($self->get_assignment('libout'));
+
+ print $fh "lib_output:\n";
+
+ if (defined $shared && $shared ne '') {
+ print $fh (defined $dllout ? $dllout : $libout), " $shared\n";
+ }
+ if ((defined $static && $static ne '') &&
+ (defined $dllout || !defined $shared ||
+ (defined $shared && $shared ne $static))) {
+ print $fh "$libout $static\n";
+ }
+ }
+
+ close($fh);
+ return 1, undef;
+ }
+
+ return 0, 'Unable write to ' . $insfile;
+}
+
+
+sub write_project {
+ my $self = shift;
+ my $status = 2;
+ my $error;
+ my $progress = $self->get_progress_callback();
+
+ &$progress() if (defined $progress);
+
+ if ($self->check_features($self->get_assignment('requires'),
+ $self->get_assignment('avoids'),
+ 1)) {
+ my $webapp = $self->get_assignment('webapp');
+ my $ntwp = $self->need_to_write_project();
+ if ($webapp || $ntwp) {
+ if ($webapp && !$self->webapp_supported()) {
+ $self->warning("Web Applications are not supported by this type.");
+ }
+ else {
+ ## A return value of 2 from need_to_write_project() indicates
+ ## that the only reason that we need to write the project is that
+ ## there are custom input files (i.e., no source or resource
+ ## files).
+ $self->process_assignment('custom_only', '1') if ($ntwp == 2);
+
+ if ($self->get_assignment('custom_only')) {
+ $self->remove_non_custom_settings();
+ }
+
+ if ($self->{'escape_spaces'}) {
+ foreach my $name ('exename', 'sharedname', 'staticname',
+ 'exeout', 'dllout', 'libout') {
+ my $value = $self->get_assignment($name);
+ if (defined $value && $value =~ s/(\s)/\\$1/g) {
+ $self->process_assignment($name, $value);
+ }
+ }
+ foreach my $key (keys %{$self->{'valid_components'}}) {
+ my $names = $self->{$key};
+ foreach my $name (keys %$names) {
+ foreach my $key (keys %{$$names{$name}}) {
+ foreach my $file (@{$$names{$name}->{$key}}) {
+ $file =~ s/(\s)/\\$1/g;
+ }
+ }
+ }
+ }
+ }
+
+ ## We don't need to pass a file name here. write_output_file()
+ ## will determine the file name for itself.
+ ($status, $error) = $self->write_output_file($webapp);
+
+ ## Write the .ins file if the user requested it and we were
+ ## successful.
+ if ($self->{'generate_ins'} && $status) {
+ ($status, $error) = $self->write_install_file();
+ }
+ }
+ }
+ elsif ($self->warn_useless_project()) {
+ my $msg = $self->transform_file_name($self->project_file_name()) .
+ " has no useful targets.";
+
+ if ($self->{'current_input'} eq '') {
+ $self->information($msg);
+ }
+ else {
+ $self->warning($msg);
+ }
+ }
+ }
+
+ return $status, $error;
+}
+
+
+sub get_project_info {
+ return $_[0]->{'project_info'};
+}
+
+
+sub get_lib_locations {
+ return $_[0]->{'lib_locations'};
+}
+
+
+sub get_inheritance_tree {
+ return $_[0]->{'inheritance_tree'};
+}
+
+
+sub set_component_extensions {
+ my $self = shift;
+ my $vc = $self->{'valid_components'};
+ my $ec = $self->{'exclude_components'};
+
+ foreach my $key (keys %$vc) {
+ my $ov = $self->override_valid_component_extensions($key,
+ @{$$vc{$key}});
+ $$vc{$key} = $ov if (defined $ov);
+ }
+
+ foreach my $key (keys %$ec) {
+ my $ov = $self->override_exclude_component_extensions($key,
+ @{$$ec{$key}});
+ $$ec{$key} = $ov if (defined $ov);
+ }
+}
+
+
+sub get_component_extensions {
+ my($self, $comp) = @_;
+ my @ext;
+ if (defined $self->{'valid_components'}->{$comp}) {
+ ## Build up an array of extensions. Since they are stored as regular
+ ## expressions, we need to remove the escaped period to provide the
+ ## minimal amount of text for each extension to provide maximum
+ ## flexibility within the project template.
+ foreach my $re (@{$self->{'valid_components'}->{$comp}}) {
+ push(@ext, $re);
+ $ext[$#ext] =~ s/\\\.//;
+ }
+ }
+ return @ext;
+}
+
+
+sub set_source_listing_callback {
+ my($self, $cb) = @_;
+ $self->{'source_callback'} = $cb;
+}
+
+
+sub reset_values {
+ my $self = shift;
+
+ ## Only put data structures that need to be cleared
+ ## out when the mpc file is done being read, not at the
+ ## end of each project within the mpc file. Those go in
+ ## the closing curly brace section of parse_line().
+ $self->{'project_info'} = [];
+ $self->{'lib_locations'} = {};
+ $self->reset_generating_types();
+}
+
+
+sub add_default_matching_assignments {
+ my $self = shift;
+ my $lang = $self->get_language();
+
+ foreach my $key (keys %{$language{$lang}->[0]}) {
+ push(@{$language{$lang}->[2]->{$key}}, @default_matching_assignments)
+ if (!StringProcessor::fgrep($default_matching_assignments[0],
+ $language{$lang}->[2]->{$key}));
+ }
+}
+
+
+sub reset_generating_types {
+ my $self = shift;
+ my $lang = $self->get_language();
+ my %reset = ('valid_components' => $language{$lang}->[0],
+ 'custom_only_removed' => $language{$lang}->[0],
+ 'exclude_components' => $language{$lang}->[1],
+ 'matching_assignments' => $language{$lang}->[2],
+ 'generated_exts' => {},
+ 'valid_names' => \%validNames,
+ );
+
+ foreach my $r (keys %reset) {
+ $self->{$r} = {};
+ foreach my $key (keys %{$reset{$r}}) {
+ $self->{$r}->{$key} = $reset{$r}->{$key};
+ }
+ }
+
+ $self->{'custom_types'} = {};
+
+ ## Allow subclasses to override the default extensions
+ $self->set_component_extensions();
+}
+
+
+sub get_template_input {
+ my $self = shift;
+ my $lang = $self->get_language();
+
+ ## This follows along the same logic as read_template_input() by
+ ## checking for exe target and then defaulting to a lib target
+ if ($self->exe_target()) {
+ if ($self->get_static() == 1) {
+ return $self->{'lib_exe_template_input'}->{$lang}->{$tikey};
+ }
+ else {
+ return $self->{'dll_exe_template_input'}->{$lang}->{$tikey};
+ }
+ }
+
+ if ($self->get_static() == 1) {
+ return $self->{'lib_template_input'}->{$lang}->{$tikey};
+ }
+
+ return $self->{'dll_template_input'}->{$lang}->{$tikey};
+}
+
+
+sub update_project_info {
+ my($self, $tparser, $append, $names, $sep) = @_;
+ my $value = '';
+ $sep = '' if (!defined $sep);
+
+ ## Append the values of all names into one string
+ my $ncount = scalar(@$names) - 1;
+ for(my $i = 0; $i <= $ncount; $i++) {
+ $value .= $self->translate_value(
+ $$names[$i],
+ $tparser->get_value_with_default($$names[$i]));
+ $value .= $sep if ($i != $ncount);
+ }
+
+ ## There may be more than one template associated with this project
+ ## creator. If there is, we can only add one generated file and we
+ ## rely on the project creator to tell us which template generates the
+ ## file that we need to track.
+ if ($self->file_visible($self->{'current_template'})) {
+ ## If we already have an array, take the one off the top. Otherwise,
+ ## create a new one which will be added below.
+ my $arr = ($append && defined $self->{'project_info'}->[0] ?
+ pop(@{$self->{'project_info'}}) : []);
+
+ ## Set up the hash table when we are starting a new project_info
+ $self->{'project_info_hash_table'} = {} if (!$append);
+
+ ## If we haven't seen this value yet, put it on the array
+ if (!defined $self->{'project_info_hash_table'}->{"@$names $value"}) {
+ $self->{'project_info_hash_table'}->{"@$names $value"} = 1;
+ push(@$arr, $value);
+ }
+
+ ## Always push the array back onto the project_info
+ push(@{$self->{'project_info'}}, $arr);
+ }
+
+ return $value;
+}
+
+
+sub adjust_value {
+ my($self, $names, $value, $tp) = @_;
+ my $atemp = $self->get_addtemp();
+
+ ## Perform any additions, subtractions
+ ## or overrides for the template values.
+ foreach my $name (@$names) {
+ if (defined $name && defined $atemp->{lc($name)}) {
+ my $lname = lc($name);
+ my $base = $lname;
+ $base =~ s/.*:://;
+
+ ## If the template variable is a complex name, then we need to make
+ ## sure that the mapped value belongs to the correct type based on
+ ## the base of the complex name. The $tp (TemplateParser) variable
+ ## will, in the majority of all calls to this method, be defined so
+ ## it is checked second to avoid checking it if the name isn't
+ ## complex.
+ if ($base =~ /(.+)\->/ && defined $tp) {
+ my $v = $tp->get_value($1);
+ if (defined $v) {
+ my $found = undef;
+ foreach my $val (@{$atemp->{$lname}}) {
+ if (defined $$val[3]) {
+ my $mapped = $self->{'valid_names'}->{$$val[3]};
+ if (defined $mapped && UNIVERSAL::isa($mapped, 'ARRAY')) {
+ $found = 1 if ($v ne $$mapped[0]);
+ }
+ last;
+ }
+ }
+ next if ($found);
+ }
+ }
+
+ my $replace = (defined $self->{'valid_names'}->{$base} &&
+ ($self->{'valid_names'}->{$base} & 0x04) == 0);
+ foreach my $val (@{$atemp->{$lname}}) {
+ if ($replace && index($$val[1], '<%') >= 0) {
+ $$val[1] = $self->replace_parameters($$val[1],
+ $self->{'command_subs'});
+ }
+ my $arr = $self->create_array($$val[1]);
+ if ($$val[0] > 0) {
+ if (!defined $value) {
+ $value = '';
+ }
+ if (UNIVERSAL::isa($value, 'ARRAY')) {
+ ## Avoid adding duplicates. If the existing array contains
+ ## the value already, remove it from the newly created array.
+ for(my $i = 0; $i < scalar(@$value); $i++) {
+ if (StringProcessor::fgrep($$value[$i], $arr)) {
+ splice(@$value, $i, 1);
+ $i--;
+ }
+ }
+
+ ## We need to make $value a new array reference ($arr)
+ ## to avoid modifying the array reference pointed to by $value
+ unshift(@$arr, @$value);
+ $value = $arr;
+ }
+ else {
+ $value .= " $$val[1]";
+ }
+ }
+ elsif ($$val[0] < 0) {
+ if (defined $value) {
+ my $parts;
+ if (UNIVERSAL::isa($value, 'ARRAY')) {
+ $parts = $value;
+ }
+ else {
+ $parts = $self->create_array($value);
+ }
+
+ $value = [];
+ foreach my $part (@$parts) {
+ if ($part ne '') {
+ push(@$value, $part) if (!StringProcessor::fgrep($part, $arr));
+ }
+ }
+ }
+ }
+ else {
+ ## If the user set the variable to empty, then we need to
+ ## set the value to undef
+ $value = (defined $$arr[0] ? $arr : undef);
+ }
+ }
+ last;
+ }
+ }
+
+ return $value;
+}
+
+
+sub get_verbatim {
+ my($self, $marker) = @_;
+ my $str;
+ my $thash = $self->{'verbatim'}->{$self->{'pctype'}};
+
+ if (defined $thash) {
+ if (defined $thash->{$marker}) {
+ my $crlf = $self->crlf();
+ foreach my $line (@{$thash->{$marker}}) {
+ $str = '' if (!defined $str);
+ $str .= $self->process_special($line) . $crlf;
+ }
+ if (defined $str) {
+ $str .= $crlf;
+ $self->{'verbatim_accessed'}->{$self->{'pctype'}}->{$marker} = 1;
+ }
+ }
+ }
+
+ return $str;
+}
+
+
+sub generate_recursive_input_list {
+ my($self, $dir, $exclude) = @_;
+ return $self->extension_recursive_input_list($dir,
+ $exclude,
+ $ProjectCreatorExtension);
+}
+
+
+sub get_modified_project_file_name {
+ my($self, $name, $ext) = @_;
+ my $nmod = $self->get_name_modifier();
+
+ ## We don't apply the name modifier to the project file
+ ## name if we have already applied it to the project name
+ ## since the project file name comes from the project name.
+ if (defined $nmod && !$self->get_apply_project()) {
+ $nmod =~ s/\*/$name/g;
+ $name = $nmod;
+ }
+ return "$name$ext";
+}
+
+
+sub get_valid_names {
+ return $_[0]->{'valid_names'};
+}
+
+
+sub get_feature_parser {
+ return $_[0]->{'feature_parser'};
+}
+
+
+sub preserve_assignment_order {
+ my($self, $name) = @_;
+ my $mapped = $self->{'valid_names'}->{$name};
+
+ ## Only return the value stored in the valid_names hash map if it's
+ ## defined and it's not an array reference. The array reference is
+ ## a keyword mapping and all mapped keywords should have preserved
+ ## assignment order.
+ if (defined $mapped && !UNIVERSAL::isa($mapped, 'ARRAY')) {
+ return ($mapped & 1);
+ }
+
+ return 1;
+}
+
+
+sub add_to_template_input_value {
+ my($self, $name) = @_;
+ my $mapped = $self->{'valid_names'}->{$name};
+
+ ## Only return the value stored in the valid_names hash map if it's
+ ## defined and it's not an array reference. The array reference is
+ ## a keyword mapping and no mapped keywords should be added to
+ ## template input variables.
+ if (defined $mapped && !UNIVERSAL::isa($mapped, 'ARRAY')) {
+ return ($mapped & 2);
+ }
+
+ return 0;
+}
+
+
+sub dependency_combined_static_library {
+ #my $self = shift;
+ return defined $ENV{MPC_DEPENDENCY_COMBINED_STATIC_LIBRARY};
+}
+
+
+sub translate_value {
+ my($self, $key, $val) = @_;
+
+ if ($key eq 'after' && $val ne '') {
+ my $arr = $self->create_array($val);
+ $val = '';
+
+ if ($self->require_dependencies()) {
+ foreach my $entry (@$arr) {
+ if ($self->get_apply_project()) {
+ my $nmod = $self->get_name_modifier();
+ if (defined $nmod) {
+ $nmod =~ s/\*/$entry/g;
+ $entry = $nmod;
+ }
+ }
+ $val .= '"' . ($self->dependency_is_filename() ?
+ $self->project_file_name($entry) : $entry) . '" ';
+ }
+ $val =~ s/\s+$//;
+ }
+ }
+ return $val;
+}
+
+
+sub requires_parameters {
+ #my $self = shift;
+ #my $name = shift;
+ return $custom{$_[1]};
+}
+
+
+sub project_file_name {
+ my($self, $name, $template) = @_;
+
+ ## Fill in the name if one wasn't provided
+ $name = $self->get_assignment('project_name') if (!defined $name);
+
+ return $self->get_modified_project_file_name(
+ $self->project_file_prefix() . $name,
+ $self->project_file_extension());
+}
+
+
+sub remove_non_custom_settings {
+ my $self = shift;
+
+ ## Remove any files that may have automatically been added
+ ## to this project
+ foreach my $key (keys %{$self->{'custom_only_removed'}}) {
+ $self->{$key} = {};
+ }
+
+ ## Unset the exename, sharedname and staticname
+ $self->process_assignment('exename', undef);
+ $self->process_assignment('sharedname', undef);
+ $self->process_assignment('staticname', undef);
+}
+
+
+sub remove_wanted_extension {
+ my($self, $name, $array) = @_;
+
+ foreach my $wanted (@$array) {
+ return $name if ($name =~ s/$wanted$//);
+ }
+
+ ## If the user provided file does not match any of the
+ ## extensions specified by the custom definition, we need
+ ## to remove the extension or else this file will not be
+ ## added to the project.
+ $name =~ s/\.[^\.]+$//;
+ return $name;
+}
+
+
+sub resolve_alias {
+ if (index($_[1], 'install') >= 0) {
+ my $resolved = $_[1];
+ if ($resolved =~ s/(.*::)install$/$1exeout/) {
+ }
+ elsif ($resolved eq 'install') {
+ $resolved = 'exeout';
+ }
+ return $resolved;
+ }
+ return $_[1];
+}
+
+
+sub create_feature_parser {
+ my($self, $features, $feature) = @_;
+ my $gfeature = $self->{'gfeature_file'};
+ my $typefeaturef = (defined $gfeature ?
+ $self->mpc_dirname($gfeature) . '/' : '') .
+ $self->{'pctype'} . '.features';
+ $typefeaturef = undef if (! -r $typefeaturef);
+ if (defined $feature && $feature !~ /[\/\\]/i) {
+ my $searched = $self->search_include_path($feature);
+ $feature = $searched if (defined $searched);
+ }
+ my $fp = new FeatureParser($features,
+ $gfeature,
+ $typefeaturef,
+ $feature);
+
+ my $slo = $fp->get_value($static_libs_feature);
+ if (!defined $slo) {
+ my $sval = $self->get_static() || 0;
+ $fp->parse_line(undef,
+ $static_libs_feature . ' = ' . $sval);
+ }
+
+ return $fp;
+}
+
+
+sub restore_state_helper {
+ my($self, $skey, $old, $new) = @_;
+
+ if ($skey eq 'feature_file') {
+ if ($self->{'features_changed'} ||
+ !(!defined $old && !defined $new ||
+ (defined $old && defined $new && $old eq $new))) {
+ ## Create a new feature parser. This relies on the fact that
+ ## 'features' is restored first in restore_state().
+ $self->{'feature_parser'} = $self->create_feature_parser(
+ $self->get_features(), $new);
+ $self->{'features_changed'} = undef;
+ }
+ }
+ elsif ($skey eq 'ti') {
+ my $lang = $self->get_language();
+ my @keys = keys %$old;
+ @keys = keys %$new if (!defined $keys[0]);
+ foreach my $key (@keys) {
+ if (!defined $$old{$key} || !defined $$new{$key} ||
+ $$old{$key} ne $$new{$key}) {
+ ## Clear out the template input reader that we're currently set
+ ## to use.
+ $self->{$key . '_template_input'}->{$lang}->{$tikey} = undef;
+ }
+ }
+ }
+ elsif ($skey eq 'features') {
+ ## If the user has changed the 'features' setting, then we need to
+ ## make sure that we create a new feature parser regardless of
+ ## whether or not the feature file has changed.
+ $self->{'features_changed'} = ("@$old" ne "@$new");
+ }
+ elsif ($skey eq 'language') {
+ if ($old ne $new) {
+ $self->add_default_matching_assignments();
+ }
+ }
+}
+
+
+sub get_initial_relative_values {
+ return $_[0]->{'expanded'}, 1;
+}
+
+sub add_main_function {
+ my $langmain = shift;
+
+ ## See if a language was supplied.
+ if ($langmain =~ /([^:]+):(.+)/) {
+ ## If the language supplied is not one that we know about, return an
+ ## error message.
+ return 'Invalid language: ' . $1 if (!defined $language{$1});
+
+ ## Otherwise, add it to the list for the language.
+ push(@{$mains{$1}}, $2);
+ }
+ else {
+ ## No language was supplied, so add the main to all of the languages
+ ## that we support.
+ foreach my $lang (keys %language) {
+ push(@{$mains{$lang}}, $langmain);
+ }
+ }
+
+ ## Return no error message.
+ return undef;
+}
+
+sub get_resource_tag {
+ my $self = shift;
+ my $lang = $self->get_language();
+
+ ## Not all entries in the %language map have a resource tag.
+ ## For this, we will just return the tag for C++ since it probably
+ ## doesn't really matter anyway.
+ return defined $language{$lang}->[5] ? $language{$lang}->[5] : $cppresource;
+}
+
+# ************************************************************
+# Accessors used by support scripts
+# ************************************************************
+
+sub getKeywords {
+ return \%validNames;
+}
+
+sub getValidComponents {
+ my $language = shift;
+ return (defined $language{$language} ? $language{$language}->[0] : undef);
+}
+
+# ************************************************************
+# Virtual Methods To Be Overridden
+# ************************************************************
+
+sub languageSupported {
+ #my $self = shift;
+ return $_[0]->get_language() eq Creator::cplusplus;
+}
+
+sub file_visible {
+ #my($self, $template) = @_;
+ return 1;
+}
+
+sub webapp_supported {
+ #my $self = shift;
+ return 0;
+}
+
+
+sub use_win_compatibility_commands {
+ #my $self = shift;
+ return $ENV{MPC_USE_WIN_COMMANDS};
+}
+
+
+sub post_file_creation {
+ #my $self = shift;
+ #my $file = shift;
+}
+
+
+sub escape_spaces {
+ #my $self = shift;
+ return 0;
+}
+
+
+sub validated_directory {
+ my($self, $dir) = @_;
+ return $dir;
+}
+
+sub get_quote_symbol {
+ #my $self = shift;
+ return '"';
+}
+
+sub get_escaped_quote_symbol {
+ #my $self = shift;
+ return '\\\"';
+}
+
+sub get_gt_symbol {
+ #my $self = shift;
+ return '>';
+}
+
+
+sub get_lt_symbol {
+ #my $self = shift;
+ return '<';
+}
+
+
+sub get_and_symbol {
+ #my $self = shift;
+ return '&&';
+}
+
+
+sub get_or_symbol {
+ #my $self = shift;
+ return '||';
+}
+
+
+sub get_cmdsep_symbol {
+ #my $self = shift;
+ return ';';
+}
+
+
+sub dollar_special {
+ #my $self = shift;
+ return 0;
+}
+
+
+sub expand_variables_from_template_values {
+ #my $self = shift;
+ return 1;
+}
+
+
+sub require_dependencies {
+ #my $self = shift;
+ return 1;
+}
+
+
+sub dependency_is_filename {
+ #my $self = shift;
+ return 1;
+}
+
+
+sub fill_value {
+ #my $self = shift;
+ #my $name = shift;
+ return undef;
+}
+
+
+sub project_file_prefix {
+ #my $self = shift;
+ return '';
+}
+
+
+sub project_file_extension {
+ #my $self = shift;
+ return '';
+}
+
+
+sub override_valid_component_extensions {
+ #my $self = shift;
+ #my $comp = shift;
+ return undef;
+}
+
+
+sub override_exclude_component_extensions {
+ #my $self = shift;
+ #my $comp = shift;
+ return undef;
+}
+
+
+sub get_dll_exe_template_input_file {
+ #my($self, $tkey) = @_;
+ return undef;
+}
+
+
+sub get_lib_exe_template_input_file {
+ my($self, $tkey) = @_;
+ return $self->get_dll_exe_template_input_file($tkey);
+}
+
+
+sub get_lib_template_input_file {
+ my($self, $tkey) = @_;
+ return $self->get_dll_template_input_file($tkey);
+}
+
+
+sub get_dll_template_input_file {
+ #my($self, $tkey) = @_;
+ return undef;
+}
+
+
+sub get_template {
+ return $_[0]->{'pctype'};
+}
+
+sub requires_forward_slashes {
+ return 0;
+}
+
+sub warn_useless_project {
+ return 1;
+}
+
+sub get_properties {
+ my $self = shift;
+ return {'static' => $self->get_static(),
+ $self->get_language() => 1};
+}
+
+1;