package CMakeWorkspaceCreator; # ************************************************************ # Description : A CMake Workspace creator # Author : Chad Elliott # Create Date : 10/10/2022 # ************************************************************ # ************************************************************ # Pragmas # ************************************************************ use strict; use File::Basename; use CMakeProjectCreator; use WorkspaceCreator; use vars qw(@ISA); @ISA = qw(WorkspaceCreator); # ************************************************************ # Data Section # ************************************************************ my $version = '3.12.0'; # ************************************************************ # Subroutine Section # ************************************************************ sub workspace_per_project { #my $self = shift; return 1; } sub workspace_file_name { return 'CMakeLists.txt'; } sub pre_workspace { my($self, $fh) = @_; my $crlf = $self->crlf(); $self->print_workspace_comment($fh, '# CMake Workspace', $crlf, '#', $crlf, '# This file was generated by MPC.', $crlf, '#', $crlf, '# MPC Command:', $crlf, '# ', $self->create_command_line_string($0, @ARGV), $crlf, $crlf); } sub out_of_tree { my($self, $dir) = @_; return ($dir =~ /^\.\.\// || !$self->path_is_relative($dir)); } ## Get the top level directory in the path. If the path does not contain ## a directory, the path will be returned unmodified. sub get_top_directory { my($self, $path) = @_; ## First, convert the path to a relative path based on the current working ## directory. my $dir = $self->path_to_relative($self->getcwd(), $path); if ($self->out_of_tree($dir)) { ## If the directory is above the current directory or not relative to ## the current working directory, we need to give the directory portion ## back and call it a day. return $self->mpc_dirname($dir); } else { my $done = 0; do { ## Go up one directory. If we were already at the top directory, ## we're finished. my $next = $self->mpc_dirname($dir); if ($next eq '.') { $done = 1; } else { $dir = $next; } } while(!$done); } return $dir; } sub write_ws_top { my($self, $fh, $ws) = @_; my $crlf = $self->crlf(); print $fh "cmake_minimum_required(VERSION $version)", $crlf, "project($ws CXX)", $crlf; } sub write_include_ws { my($self, $prjs) = @_; my $fh = new FileHandle(); my $dir = $self->mpc_dirname($$prjs[0]); my $file = $dir . '/' . $self->workspace_file_name(); if (open($fh, ">$file")) { my $crlf = $self->crlf(); $self->pre_workspace($fh); $self->write_ws_top($fh, basename($dir)); foreach my $prj (@$prjs) { print $fh "${crlf}include(", basename($prj), ")"; } print $fh $crlf; close($fh); } } sub write_comps { my($self, $fh, $creator) = @_; my $status = 1; my $errorString = ''; my @project_dirs; my @projects = $self->sort_dependencies($self->get_projects(), 0); ## Build a list of top level directories. We only want to go down one ## directory. The workspace in that directory will handle going to ## other subdirectories. my %dirs; my %out_of_tree; foreach my $entry (@projects) { my $dir = $self->get_top_directory($entry); if ($dir ne $entry) { if (!exists $dirs{$dir}) { ## Keep track of the project existing in this directory $dirs{$dir} = 1; push(@project_dirs, $dir); } ## If this directory is out-of-tree, it will not contain a top-level ## workspace (due to the way that workspace-per-directory works). We ## need to keep track of it here. if ($self->out_of_tree($dir)) { if (exists $out_of_tree{$dir}) { push(@{$out_of_tree{$dir}}, $entry); } else { $out_of_tree{$dir} = [$entry]; } } } } ## Create the basis of a project so that we can add our add_subdirectory() ## calls below it. my $crlf = $self->crlf(); my $ws = TemplateParser::actual_normalize(undef, $self->get_workspace_name()); $self->write_ws_top($fh, $ws); my $first = 1; my %bin_used; foreach my $dir (@project_dirs) { if ($first) { $first = undef; print $fh $crlf; } my $bin_dir = ''; if (exists $out_of_tree{$dir}) { ## Because this directory is out-of-tree, CMake requires a binary ## directory to be passed to add_subdirectory(). my $bin = basename($dir); while(exists $bin_used{$bin}) { $bin .= '_'; } $bin_dir = " $bin"; $bin_used{$bin} = 1; $self->write_include_ws($out_of_tree{$dir}); } print $fh "add_subdirectory($dir$bin_dir)$crlf"; } $first = 1; foreach my $entry (@projects) { my $dir = $self->mpc_dirname($entry); if ($dir eq '.') { if ($first) { $first = undef; print $fh $crlf; } print $fh "include($entry)$crlf"; } } return $status, $errorString; } 1;