# The OlympusBuild build module # See http://wiki.speech.cs.cmu.edu/olympus/index.php/Build_Module use strict; use Getopt::Long; use File::Spec; use IO::Handle; package OlympusBuild; use constant PLATFORM_WIN32 => 0; use constant PLATFORM_MACOSX => 1; use constant PLATFORM_LINUX => 2; use constant PLATFORM_OTHER => 3; my $logiosResult = 1; #writes a message to stderr and the log file as well sub pLog { print @_; print BUILD_LOG @_; } #Adds a message to the summarization and logs it via pLog sub remember { my $self = shift; my $message = shift; $self->{'summary'} .= $message; pLog $message; } #summarize the build experience sub summarize { my $self = shift; pLog "*********************************************************************$/"; pLog "SUMMARY:$/"; pLog $self->{'summary'}.$/; if ($logiosResult == 0) { my $grammarLogFile = File::Spec->catfile('Resources', 'make_language.log'); pLog("Grammar generation failed.$/See $grammarLogFile for more information.$/$/"); } pLog sprintf("Total Build Time: %d:%02d$/", $self->{'build_time'}/60, $self->{'build_time'}%60); pLog "*********************************************************************$/"; } sub new { my $class = shift; my %params = @_; #process cmd line style switches into params #just include an 'ARGV' hash refernce to @ARGV my $ok = Getopt::Long::GetOptions(\%params, 'NO_SET_OLYMPUS_ROOT!', 'BUILD_CONF=s'); #for my $arg (@{$params{'ARGV'}}) { # $params{$1} = 1 if $arg =~ m/--(.+)/ && !defined $params{$1}; #} my $objref = {'PROJECT' => $params{'PROJECT'}, 'PROJECT_ROOT' => (defined $params{'PROJECT'}? File::Spec->rel2abs($params{'PROJECT_ROOT'} || File::Spec->curdir): undef), 'OLYMPUS_ROOT' => (defined $params{'PROJECT'}? $params{'OLYMPUS_ROOT'} || $ENV{'OLYMPUS_ROOT'}: File::Spec->curdir), 'BUILD_TYPE' => $params{'BUILD_TYPE'} || 'Build', 'BUILD_CONF' => $params{'BUILD_CONF'} || 'Release', 'BUILD_LOG' => $params{'BUILD_LOG'} || 'build.log', 'NO_INTERACTION' => $params{'no_interaction'}, 'NO_SET_OLYMPUS_ROOT' => $params{'no_set_olympus_root'}, 'CMAKE_PARAMS' => $params{'CMAKE_PARAMS'} || "" }; open(BUILD_LOG, '>'.${$objref}{'BUILD_LOG'}) || die "Can't open build log file '${$objref}{BUILD_LOG}': $!$/"; BUILD_LOG->autoflush(1); #internal modules need to be included here since they need to know olympus_root $objref->{'LOGIOS_ROOT'} = File::Spec->catdir($objref->{'OLYMPUS_ROOT'}, 'Tools', 'logios'); require File::Spec->catfile($objref->{'LOGIOS_ROOT'}, 'scripts', 'Logios.pm'); for my $param (keys %$objref) { pLog "$param... ${$objref}{$param}$/" if defined ${$objref}{$param}; } bless $objref, $class; } #builds the Olympus framework sub buildOlympus { my $self = shift; if ($self->configure(@_)) { $self->setEnv('OLYMPUS_ROOT', File::Spec->rel2abs($self->{'OLYMPUS_ROOT'})) unless $self->{'NO_SET_OLYMPUS_ROOT'}; $self->generateSVNDataHeader; $self->buildSAPI; pLog $/; return $self->buildAgents; } $self->remember("Error during configure stage. Please fix and run the script again.$/"); return 0; } #builds an Olympus system #returns 1 on success, 0 on failure sub buildSystem { my $self = shift; if ($self->configure(@_)) { return $self->buildAgents; } $self->remember("Error during configure stage. Please fix and run the script again.$/"); return 0; } sub buildVizualization { my $self = shift; my $success = 1; my $start_time = time; my $RCTaskViz_root = File::Spec->catdir($self->{'OLYMPUS_ROOT'}, 'Tools', 'RCTaskViz'); require File::Spec->catfile($RCTaskViz_root, 'RCTaskViz.pm'); my $grammar_root = File::Spec->catfile($self->{'PROJECT_ROOT'}, 'Resources', 'Grammar', 'GRAMMAR', "$self->{'PROJECT'}Task"); my $rctaskviz = RCTaskViz->new('RCTASKVIZ_ROOT' => File::Spec->catdir($self->{'OLYMPUS_ROOT'}, 'Tools', 'RCTaskViz'), 'PHOENIX_GRAMMAR' => "$grammar_root.gra", 'PHOENIX_FORMS' => "$grammar_root.forms"); $rctaskviz->viz(File::Spec->catfile($self->{'PROJECT_ROOT'}, 'Agents', 'RavenClawDM', 'DialogTask.cpp')); $self->{'build_time'} += time - $start_time; return $success; } #builds the SAPI components for Olympus #returns 1 on success, 0 on failure sub buildSAPI { my $self = shift; if (!defined $self->{'lastConfigureResult'}) { $self->configure; } my $success = 0; if ($self->{'lastConfigureResult'} && defined $self->{'SAPISimpleAudioSln'}) { my $start_time = time; $success = $self->buildVSSolution($self->{'SAPISimpleAudioSln'}); $self->remember("Problem building the required SAPI SimpleAudio DLL example.$/Was it installed correctly?$/$/") if !$success; $self->{'build_time'} += time - $start_time; } return $success; } #builds an Olympus Agents solution file #returns 1 on success, 0 on failure sub buildAgents { my $self = shift; if (!defined $self->{'lastConfigureResult'}) { $self->configure; } my $success = 0; if ($self->{'lastConfigureResult'}) { my $start_time = time; my $solution_file = $self->findSolution; if ($self->{'usingCMake'}) { chdir(File::Spec->catdir('build','cmake')); $self->cleanCMakeFiles if $self->{'BUILD_TYPE'}eq 'Rebuild'; $self->{'orig_build_type'} = $self->{'BUILD_TYPE'}; $self->{'BUILD_TYPE'} = 'Build'; $self->runCMake; $solution_file = $self->findCMakeSolution; if (!$solution_file) { $self->remember("CMake did not generate a Visual Studio solution$/"); $success = 0; chdir(File::Spec->catdir('..','..')); } else { $success = $self->buildVSSolution($solution_file); $self->remember("Problem building Olympus Agents using CMake$/") if !$success; chdir(File::Spec->catdir('..','..')); $success &= $self->copyReleaseFilesToBinX86Nt; } $self->{'BUILD_TYPE'} = $self->{'orig_build_type'}; } else { $success = $self->buildVSSolution($self->findSolution); $self->remember("Problem building Olympus Agents$/") if !$success; } pLog $/; if (!defined $self->{'PROJECT'}) { if (!$self->buildAnt(File::Spec->catfile('Libraries', 'libOlympusUtility', 'javaLibOlympusUtility', 'build.xml'))) { $self->remember("Problem building optional javaLibOlympusUtility component$/"); $success = 0; } if (!$self->buildAnt(File::Spec->catfile('Agents', 'JavaTTY', 'build.xml'))) { $self->remember("Problem building optional JavaTTY component$/"); $success = 0; } if (!$self->buildAnt(File::Spec->catfile('Agents', 'MultiDecoder', 'AMSwitcher', 'build.xml'))) { $self->remember("Problem building optional AMSwitcher component$/"); $success = 0; } if (!$self->buildAnt(File::Spec->catfile('Agents', 'File2GFrames', 'build.xml'))) { $self->remember("Problem building optional File2GFrames component$/"); $success = 0; } } $self->{'build_time'} += time - $start_time; } return $success; } sub runCMake { my $self = shift; my $cmakeLoc = $self->{'cmake'}; if (defined $cmakeLoc && $cmakeLoc) { if ($self->{'platform'} eq OlympusBuild::PLATFORM_WIN32) { if ($self->{'BuildSysVer'} == 2005) { return $self->runProgram("\"$cmakeLoc\" -G \"Visual Studio 8 2005\" $self->{'CMAKE_PARAMS'} ../.."); } elsif ($self->{'BuildSysVer'} == 2008) { return $self->runProgram("\"$cmakeLoc\" -G \"Visual Studio 9 2008\" $self->{'CMAKE_PARAMS'} ../.."); } } elsif ($self->{'platform'} eq OlympusBuild::PLATFORM_MACOSX) { return $self->runProgram("\"$cmakeLoc\" -G Xcode $self->{'CMAKE_PARAMS'} ../.."); } elsif (($self->{'platform'} eq OlympusBuild::PLATFORM_LINUX) or ($self->{'platform'} eq OlympusBuild::PLATFORM_OTHER)) { return $self->runProgram("\"$cmakeLoc\" -G \"Unix Makefiles\" $self->{'CMAKE_PARAMS'} ../.."); } } } sub findCMakeSolution { my @files = <*.sln>; foreach my $file (@files) { return $file; } return 0; } sub cleanCMakeFiles { my $self = shift; my @files = <*>; foreach my $file (@files) { if (-d $file){ $self->removeDir($file); } else { unlink($file) } } return 0; } sub removeDir { my $self = shift; my $dir = shift; local *DIR; if (!(opendir DIR, $dir)) { $self->remember("Could not open $dir for reading.$/Do you have permission to read it's contents?$/"); return; } for (readdir DIR) { next if /^\.{1,2}$/; my $path = "$dir/$_"; unlink $path if -f $path; $self->removeDir($path) if -d $path; } closedir DIR; rmdir $dir or $self->remember("Could not remove $dir.$/Was it properly emptied?$/"); } sub copyReleaseFilesToBinX86Nt { my $self = shift; my $binConfigDir = File::Spec->catdir('bin', $self->{'BUILD_CONF'}); my $binX86NtDir = File::Spec->catdir('bin','x86-nt'); pLog("$/Copying compiled executables and libraries to $binX86NtDir.... "); if (!(opendir DIR, $binConfigDir)) { $self->remember("Could not open $binConfigDir for reading.$/Do you have permission to read it's contents?$/"); return; } my $success = 1; my $releaseDirFile; for (readdir DIR) { next if /^\.{1,2}$/ || (!(/\.exe$/) && !(/\.dll$/) && !(/\.lib$/) && !(/\.so$/) && !(/\.a$/)); $releaseDirFile = File::Spec->catfile($binConfigDir, $_); my $binX86NtDirFile; if (!($self->{'BUILD_CONF'} eq 'Debug')) { $binX86NtDirFile = File::Spec->catfile($binX86NtDir, $_); } else { my $fileName = $_; $fileName =~ s/\.exe$/-DEBUG\.exe/; $binX86NtDirFile = File::Spec->catfile($binX86NtDir, $fileName); } if (!File::Copy::copy($releaseDirFile, $binX86NtDirFile)) { if ($success) { pLog("Failed.$/"); $success = 0; $self->remember("Could not copy all executables and libraries in $binConfigDir to $binX86NtDir.$/Do you have permission to copy the files?$/"); } pLog("Could not copy $releaseDirFile to$/$binX86NtDirFile$/Do you have permission to copy the file?$/"); } } closedir DIR; if ($success) { pLog("Done$/"); return 1; } return 0; } #builds an ant script (common java build env) #takes the ant script as a paramter #returns 1 on success, 0 on failure sub buildAnt { my $self = shift; if (!defined $self->{'lastConfigureResult'}) { $self->configure; } if ($self->{'lastConfigureResult'}) { my $buildfile = shift; my $success = 0; my $start_time = time; my $ant = $self->{'Ant'}; my $antTarget = ($self->{'BUILD_TYPE'} eq 'Rebuild'? 'clean default': 'default'); if (defined $ant) { if(!($success = !$self->runProgram("\"$ant\" -buildfile $buildfile $antTarget"))) { $self->remember("Problem building Ant script: $buildfile$/"); } } $self->{'build_time'} += time - $start_time; return $success; } return 0; } #build a visual studio solution #takes a solution and optional build_type and build_conf paramters #returns 1 on success, 0 on failure sub buildVSSolution { my $self = shift; if (!defined $self->{'lastConfigureResult'}) { $self->configure; } my $success = 0; if ($self->{'lastConfigureResult'}) { my $solution = shift; #Need to replace Olympus Root variables in the solution. #Although the IDE understands those variables, vcbuild does not. my $tmp = "$solution.tmp"; my ($volume, $path, $solutionName) = File::Spec->splitpath($solution); if(defined $self->{'PROJECT'}) { my $replacement = File::Spec->abs2rel($ENV{'OLYMPUS_ROOT'}, File::Spec->catpath($volume, $path)); pLog "Replacing \%OLYMPUS_ROOT\% with $replacement in solution...$/"; File::Copy::copy($solution, $tmp); if(!(open(ORIG_S, $tmp) && open(NEW_S, ">$solution"))) { remember "Problem opening $tmp for reading and/or $solution for writing$/"; return 0; } for my $line () { $line =~ s/\%OLYMPUS_ROOT\%/$replacement/; print NEW_S $line; } } my $params = shift; my $build_type = ${$params}{'BUILD_TYPE'} || $self->{'BUILD_TYPE'}; my $build_conf = ${$params}{'BUILD_CONF'} || $self->{'BUILD_CONF'}; $build_conf .= '|Win32'; my $msdev = $self->{'BuildSysExe'}; my $cmd = "\"$msdev\" \"$solution\" /M2"; $cmd .= ' /rebuild' if $build_type eq 'Rebuild'; $cmd .= " \"$build_conf\""; if ($self->runProgram($cmd, 'VSProjectFilter')) { $self->remember("Problem building Visual Studio Solution: $solutionName.$/"); } else { $success = 1; } File::Copy::move($tmp, $solution) if defined $self->{'PROJECT'}; } return $success; } sub buildResources { my $self = shift; if (!defined $self->{'lastConfigureResult'}) { $self->configure; } if ($self->{'lastConfigureResult'}) { return $self->buildResources_logios(@_); } return 0; } sub buildResources_logios { my $self = shift; my %override = @_; $logiosResult = 0; my $start_time = time; my $logios = Logios->new('OLYMODE' => 1, 'LOGIOS' => $override{'LOGIOS'} || File::Spec->catdir($self->{'OLYMPUS_ROOT'}, 'Tools', 'logios'), 'PROJECT' => $override{'PROJECT'} || "$self->{'PROJECT'}Task", 'AUX_PROJECTS' => defined $override{'AUX_PROJECTS'}? $override{'AUX_PROJECTS'}: ['Generic'], 'RESOURCES' => $override{'RESOURCES'} || File::Spec->catdir($self->{'PROJECT_ROOT'}, 'Resources'), 'INSTANCE' => $override{'INSTANCE'} || $self->{'PROJECT'}, 'FORCE' => defined $override{'FORCE'}? $override{'FORCE'}: ($self->{'BUILD_TYPE'} =~ /Rebuild/i? 1: 0), 'POCKET' => 1, # this makes the language model .ctl file use pocketsphinx format (pocketsphinx is the default decoder) ); $logios->compile_grammar; $logios->makelm; $logios->makedict; $logiosResult = 1; $self->{'build_time'} += time - $start_time; return 1; } sub generateSVNDataHeader { my $self = shift; if (defined $self->{'TortoiseWCRev'}) { if ($self->runProgram("\"$self->{'TortoiseWCRev'}\" \"$ENV{'OLYMPUS_ROOT'}\" Build\\SVN_Data_Template.h Build\\SVN_Data.h -f")) { # TortiseSVN's WC Rev returned 0 (failure) $self->remember("Writing the current checked out SVN branch and revision to$/Build\\SVN_Data.h failed. As a result, this information will not$/appear in system log files.$/$/"); open(DATA_TEMPLATE, File::Spec->catfile('Build', 'SVN_Data_Template.h')); open(DATA_HEADER, '>'.File::Spec->catfile('Build', 'SVN_Data.h')); while() { s/\$WCURL\$/UNKNOWN/; s/\$WCREV\$/UNKNOWN/; print DATA_HEADER $_; } close DATA_TEMPLATE; close DATA_HEADER; } pLog("$/"); } elsif (defined $self->{'SVNExe'}) { my $headerPath = File::Spec->catfile('Build', 'SVN_Data.h'); pLog("Writing out SVN checkout information to $headerPath...$/"); #write svn data file open(SVN_DATA, "\"$self->{'SVNExe'}\" info|"); my $url; my $rev; my $success = 1; while() { chomp; $url = $_ if s/URL: //; $rev = $_ if s/Last Changed Rev: //; } if (!defined $url || !defined $rev) { $url = "UNKNOWN"; $rev = "UNKNOWN"; $self->remember("Writing the current checked out SVN branch and revision to$/Build\\SVN_Data.h failed. As a result, this information will not$/appear in system log files.$/$/"); $success = 0; } close(SVN_DATA); open(DATA_TEMPLATE, File::Spec->catfile('Build', 'SVN_Data_Template.h')); open(DATA_HEADER, '>'.File::Spec->catfile('Build', 'SVN_Data.h')); while() { s/\$WCURL\$/$url/; s/\$WCREV\$/$rev/; print DATA_HEADER $_; } close DATA_TEMPLATE; close DATA_HEADER; if ($success) { pLog("Done.$/$/"); } } else { open(DATA_TEMPLATE, File::Spec->catfile('Build', 'SVN_Data_Template.h')); open(DATA_HEADER, '>'.File::Spec->catfile('Build', 'SVN_Data.h')); while() { s/\$WCURL\$/UNKNOWN/; s/\$WCREV\$/UNKNOWN/; print DATA_HEADER $_; } close DATA_TEMPLATE; close DATA_HEADER; } } sub DESTROY { my $self = shift; return if defined $self->{'NO_CLEANUP'}; $self->summarize; print "See log file '$self->{BUILD_LOG}'$/"; $self->broadcastEnvChange; } sub pause { my $self = shift; if (!$self->{'NO_INTERACTION'}) { print "Press ENTER to exit. "; ; } } sub configure { my $self = shift; my $valToReturn = 1; $self->findPlatform(); $valToReturn = $valToReturn && $self->findFileSpec(); $valToReturn = $valToReturn && $self->findFileCopy(); $valToReturn = $valToReturn && $self->findIPCOpen3(); $valToReturn = $valToReturn && $self->findIOHandle(); $valToReturn = $valToReturn && $self->findGetoptLong(); if ($self->{'platform'} eq $self->PLATFORM_WIN32) { $valToReturn = $self->findWin32TieRegistry(); if (!$valToReturn) { $self->remember("Configure cannot complete without Win32::TieRegistry.$/"); $self->{'lastConfigureResult'} = $valToReturn; return $valToReturn; } use constant HWND_BROADCAST => -1; use constant WM_SETTINGCHANGE => 0x1a; $valToReturn = $valToReturn && $self->findWin32API(); if (!defined $self->{'PROJECT'} && !($self->{'CMAKE_PARAMS'} =~ /KALLIOPE_USE_SAPI:BOOL=no/)) { $valToReturn = $valToReturn && $self->findSAPI(); } } $valToReturn = $valToReturn & $self->createBinX86Nt; if (!defined $self->{'PROJECT'}) { $self->findSVN; } else { $valToReturn = $valToReturn & $self->copyFilesFromOlympus; } $valToReturn = $valToReturn & $self->findCMakeIfNecessary(); $valToReturn = $valToReturn & $self->findBuildSystem(@_); $valToReturn = $valToReturn & $self->findJava(); $valToReturn = $valToReturn & $self->findAnt(); pLog("$/"); $self->{'lastConfigureResult'} = $valToReturn; return $valToReturn; } sub createBinX86Nt { my $self = shift; my $releaseDir = File::Spec->catdir('bin', 'x86-nt'); pLog("Creating $releaseDir directory for build output... "); if (!-d 'bin') { if (!mkdir('bin')) { pLog("Failed$/"); $self->remember("Could not create bin.$/Do you have permission to create a new directory in your project root?$/$/"); return 0; } } if (!-d $releaseDir) { if (!mkdir(File::Spec->catdir('bin', 'x86-nt'))) { pLog("Failed$/"); $self->remember("Could not create $releaseDir.$/Do you have permission to create a new directory in bin?$/$/"); return 0; } } pLog("Done$/"); return 1; } sub copyFilesFromOlympus { my $self = shift; my $returnVal = 1; my $releaseDir = File::Spec->catdir('bin', 'x86-nt'); if (!-d $releaseDir) { if (!$self->createBinX86Nt) { return 0; } } pLog("Copying galaxy.jar from Olympus to $releaseDir... "); if (File::Copy::copy(File::Spec->catfile($ENV{'OLYMPUS_ROOT'}, 'Libraries', 'Galaxy', 'contrib', 'MITRE', 'bindings', 'java', 'lib', 'galaxy.jar'), File::Spec->catdir('bin', 'x86-nt'))) { pLog("Done$/"); } else { pLog("Failed$/"); $self->remember("Could not copy galaxy.jar to $releaseDir.$/Do you have permission to copy galaxy.jar to $releaseDir?$/$/"); $returnVal = 0; } pLog("Copying jLibOlympusUtility.jar from Olympus to $releaseDir... "); if (File::Copy::copy(File::Spec->catfile($ENV{'OLYMPUS_ROOT'}, 'Libraries', 'libOlympusUtility', 'javalibOlympusUtility', 'dist', 'jLibOlympusUtility.jar'), File::Spec->catdir('bin', 'x86-nt'))) { pLog("Done$/"); } else { pLog("Failed$/"); $self->remember("Could not copy jLibOlympusUtility.jar to $releaseDir.$/Do you have permission to copy jLibOlympusUtility.jar to $releaseDir?$/$/"); $returnVal = 0; } pLog("Copying libOlympusUtility$self->{'sharedObjExtension'} from Olympus to $releaseDir... "); if (File::Copy::copy(File::Spec->catfile($ENV{'OLYMPUS_ROOT'}, 'bin', 'release', "libOlympusUtility$self->{'sharedObjExtension'}"), File::Spec->catdir('bin', 'x86-nt'))) { pLog("Done$/"); return $returnVal; } else { pLog("Failed$/"); $self->remember("Could not copy libOlympusUtility$self->{'sharedObjExtension'} to $releaseDir.$/Have you built libOlympusUtility$self->{'sharedObjExtension'}?$/Do you have permission to copy libOlympusUtility$self->{'sharedObjExtension'} to $releaseDir?$/$/"); return 0; } } sub findWin32API { my $self = shift; pLog('Searching for Win32::API Perl Module... '); eval { require Win32::API;}; if ($@) { pLog("Not Found$/"); $self->remember("Please install the Win32::API Perl module.$/"); pLog("$/"); return 0; } else { pLog("Found$/"); return 1; } } sub findWin32TieRegistry { my $self = shift; pLog('Searching for Win32::TieRegistry Perl Module... '); eval { require Win32::TieRegistry; $Win32::TieRegistry::Registry->Delimiter('/'); }; if ($@) { pLog("Not Found$/"); $self->remember("Please install the Win32::TieRegistry Perl module.$/"); pLog("$/"); return 0; } else { pLog("Found$/"); return 1; } } sub findFileSpec { my $self = shift; pLog('Searching for File::Spec Perl Module... '); eval { require File::Spec; }; if ($@) { pLog("Not Found$/"); $self->remember("Please install the File::Spec Perl Module.$/"); pLog("$/"); return 0; } else { pLog("Found$/"); return 1; } } sub findFileCopy { my $self = shift; pLog('Searching for File::Copy Perl Module... '); eval { require File::Copy; }; if ($@) { pLog("Not Found$/"); $self->remember("Please install the File::Copy Perl Module.$/"); pLog("$/"); return 0; } else { pLog("Found$/"); return 1; } } sub findIPCOpen3 { my $self = shift; pLog('Searching for IPC::Open3 Perl Module... '); eval { require IPC::Open3; }; if ($@) { pLog("Not Found$/"); $self->remember("Please install the IPC::Open3 Perl Module.$/"); pLog("$/"); return 0; } else { pLog("Found$/"); return 1; } } sub findIOHandle { my $self = shift; pLog('Searching for IO::Handle Perl Module... '); eval { require IO::Handle; }; if ($@) { pLog("Not Found$/"); $self->remember("Please install the IO::Handle Perl Module.$/"); pLog("$/"); return 0; } else { pLog("Found$/"); return 1; } } sub findGetoptLong { my $self = shift; pLog('Searching for Getopt::Long Perl Module... '); eval { require Getopt::Long; }; if ($@) { pLog("Not Found$/"); $self->remember("Please install the Getopt::Long Perl Module.$/"); pLog("$/"); return 0; } else { pLog("Found$/"); return 1; } } sub findPlatform() { my $self = shift; pLog('Detecting platform in use... '); # FIGURE OUT THE OS WE'RE RUNNING UNDER # Some systems support the $^O variable. If not # available then require() the Config library my $OS; unless ($OS) { unless ($OS = $^O) { require Config; $OS = $self::Config{'osname'}; } } if (($OS =~ /^MSWin/i) or ($OS =~ /^cygwin/i)) { pLog("Windows$/"); $self->{'platform'} = $self->PLATFORM_WIN32; $self->{'exeExentsion'} = ".exe"; $self->{'shellScriptExentsion'} = ".bat"; $self->{'sharedObjExtension'} = ".dll"; } elsif ($OS =~ ($OS =~ /^MacOS/i) or ($OS eq 'darwin')) { pLog("Mac OS X$/"); $self->{'platform'} = $self->PLATFORM_MACOSX; $self->{'exeExentsion'} = ""; $self->{'shellScriptExentsion'} = ".sh"; $self->{'sharedObjExtension'} = ".dylib"; } elsif ($OS eq 'linux') { pLog("Linux$/"); $self->{'platform'} = $self->PLATFORM_LINUX; $self->{'exeExentsion'} = ""; $self->{'shellScriptExentsion'} = ".sh"; $self->{'sharedObjExtension'} = ".so"; } else { pLog("Unknown/Other$/"); $self->{'platform'} = $self->PLATFORM_OTHER; $self->{'exeExentsion'} = ""; $self->{'shellScriptExentsion'} = ".sh"; $self->{'sharedObjExtension'} = ".so"; } } sub findSAPI { my $self = shift; return 1 if $self->findPlatformSDKSAPI; return 1 if $self->findSAPI_SDK51; $self->remember("Cannot find the Microsoft SAPI SDK 5.1 or higher.$/Install the SAPI 5.3 SDK by installing the Microsoft Windows SDK 6.1$/(for Windows Server 2008), along with the included Win32$/samples, from http://msdn.microsoft.com/en-us/windows/bb980924.aspx$/$/"); return 0; } sub findPlatformSDKSAPI { my $self = shift; if ($self->{'platform'} eq $self->PLATFORM_WIN32) { pLog('Searching for Microsoft Platform SDK 6.1 (for Windows Server 2008)... '); my $platformSDKKey = $Win32::TieRegistry::Registry->{'LMachine/Software/Microsoft/Microsoft SDKs/Windows/v6.1/'}; if (!defined $platformSDKKey) { pLog("Not Found$/"); return 0; } my $winSDKLibDir = $platformSDKKey->{'WinSDKBuild/InstallationFolder'}; my $winSDKSAPILib = File::Spec->catdir($winSDKLibDir, 'Lib', 'sapi.lib'); if (defined $winSDKLibDir && -e $winSDKSAPILib) { $self->{'CMAKE_PARAMS'} = $self->{'CMAKE_PARAMS'}." -D KALLIOPE_SAPI_INCLUDE_DIR=\"$winSDKLibDir". "Include\" -D KALLIOPE_SAPI_LIBRARY_FILE=\"$winSDKSAPILib\" -DKALLIOPE_SAPI_LIBRARY_VER_HEX=0x053"; $self->setEnv('SAPI_SDK_DIR', $winSDKLibDir); pLog("Found$/"); } else { pLog("Not Found$/"); $self->remember("Could not get a valid Platform SDK 6.1 SAPI library installation$/location from the registry.$/Was it installed properly?$/$/"); return 0; } pLog('Searching for the SimpleAudio SAPI Example Project... '); my $platformSDKSamplesDir = $platformSDKKey->{'WinSDKSamples/InstallationFolder'}; my $simpleAudioSampleDir = File::Spec->catdir($platformSDKSamplesDir, 'winui', 'Speech', 'SimpleAudio'); my $simpleAudioSampleSolution = File::Spec->catfile($simpleAudioSampleDir, 'SimpleAudio.sln'); if (defined $platformSDKSamplesDir && -e $simpleAudioSampleSolution) { $self->{'SAPISimpleAudioSln'} = $simpleAudioSampleSolution; $self->{'CMAKE_PARAMS'} = $self->{'CMAKE_PARAMS'}." -D KALLIOPE_SAPI_EXAMPLE_INCLUDE_DIR=\"$simpleAudioSampleDir\""; pLog("Found$/"); return 1; } else { pLog("Not Found$/"); $self->remember("Could not find the SimpleAudio DLL SAPI Example Solution.$/Did you install the Win32 examples when installing the SDK for$/Windows Server 2008?$/$/"); return 0; } } return 0; } sub findSAPI_SDK51 { my $self = shift; if ($self->{'platform'} eq $self->PLATFORM_WIN32) { pLog('Searching for Microsoft SAPI SDK... '); my $sapisdkhome; if(defined $ENV{'SAPI_SDK_DIR'}) { $sapisdkhome = $ENV{'SAPI_SDK_DIR'}; } else { my $speechKey = $Win32::TieRegistry::Registry->{'LMachine/SOFTWARE/Microsoft/Speech/'}; $sapisdkhome = $speechKey->{'/SDKPath'}; } if(-e File::Spec->catfile($sapisdkhome, 'include', 'sapi.h')) { pLog("Found$/"); $self->setEnv('SAPI_SDK_DIR', $sapisdkhome); pLog('Copying SAPI SDK Fixup... '); if(! (File::Copy::copy('Libraries\MS-SAPI\spcollec.h', "$sapisdkhome\\include") && File::Copy::copy('Libraries\MS-SAPI\sphelper.h', "$sapisdkhome\\include") && File::Copy::copy('Libraries\MS-SAPI\simpleaudio.sln', "$sapisdkhome\\Samples\\CPP\\SimpleAudioDll") && File::Copy::copy('Libraries\MS-SAPI\simpleaudio.vcproj', "$sapisdkhome\\Samples\\CPP\\SimpleAudioDll"))) { $self->remember("Can't copy SDK fixup$/"); pLog("Do you have permission to write to $sapisdkhome?$/"); return 0; } $self->{'SAPISimpleAudioSln'} = File::Spec->catdir($sapisdkhome, 'Samples', 'CPP', 'SimpleAudioDll', 'simpleaudio.sln'); pLog("Done$/"); return 1; } else { pLog("Not Found$/"); return 0; } } return 0; } sub findCMakeIfNecessary() { my $self = shift; pLog('Checking if the project is using CMake... '); if (-e "CMakeLists.txt") { pLog("Yes$/"); $self->{'usingCMake'} = 1; return $self->findCMake; } pLog("No$/"); $self->{'usingCMake'} = 0; return 1; } sub findCMake { my $self = shift; if ($self->{'platform'} eq $self->PLATFORM_WIN32) { return $self->findCMakeWin32; } } sub findCMakeWin32 { my $self = shift; pLog('Searching for CMake 2.4 or higher... '); my $kitwareKey = $Win32::TieRegistry::Registry->{'LMachine/SOFTWARE/KitWare/'}; if (!$kitwareKey) { pLog("Not Found$/"); $self->remember("LMachine/SOFTWARE/KitWare/ key: $^E$/"); $self->remember("Did you install CMake?$/http://www.cmake.org/cmake/cmake/resources/software.html$/"); return 0; } my $bestCMakeVer; for my $cmakeVer ($kitwareKey->SubKeyNames) { $cmakeVer =~ s/CMake //; $bestCMakeVer = $cmakeVer if !defined $bestCMakeVer || $cmakeVer >= $bestCMakeVer; } my $majorCMakeVer; my $minorCMakeVer; if($bestCMakeVer =~ /(\d+)\.(\d+)/) { $majorCMakeVer = $1; $minorCMakeVer = $2; } else { pLog("Can't Determine CMake Version$/"); $self->remember("LMachine/SOFTWARE/KitWare/ key: $^E$/"); $self->remember("Did you install CMake?$/http://www.cmake.org/cmake/resources/software.html$/"); return 0; } if($majorCMakeVer < 2 || ($majorCMakeVer = 2 && $minorCMakeVer <= 4)) { $self->remember("Invalid Version$/"); $self->remember("Download CMake 2.4 or higher from$/http://www.cmake.org/cmake/resources/software.html$/"); return 0; } my $bestCMakeKey = $kitwareKey->{"CMake $bestCMakeVer/"}; if (!defined $bestCMakeKey) { pLog("Can't Determine Best CMake Version$/"); $self->remember("LMachine/SOFTWARE/KitWare/ key: $^E$/"); $self->remember("Did you install CMake?$/http://www.cmake.org/cmake/resources/software.html$/"); return 0; } my $cmakeHome = $bestCMakeKey->{''}; my $cmakePath = File::Spec->catfile($cmakeHome, 'bin', 'cmake.exe'); if (!-e $cmakePath) { $self->remember("Invalid registry key value$/"); $self->remember("LMachine/SOFTWARE/KitWare/ key: $cmakePath$/"); $self->remember("Did you install CMake?$/http://www.cmake.org/cmake/resources/software.html$/"); return 0; } $self->{'cmake'} = $cmakePath; pLog("Found$/"); return 1; } sub findSVN { my $self = shift; if ($self->{'platform'} eq $self->PLATFORM_WIN32) { return $self->findSVNWin32 } } sub findSVNWin32 { my $self = shift; return 1 if $self->findTortoiseSVNWin32; return 1 if $self->findCollabNetSVNWin32; return 1 if $self->findTigrisSVNWin32; return 1 if $self->findSlikSVNWin32; $self->remember("Could not find a Subversion client.$/As a result, the current checked out SVN branch and revision$/will not appear in log files.$/$/"); return 0; } sub findTortoiseSVNWin32 { my $self = shift; pLog('Searching for TortoiseSVN... '); my $tortoiseKey = $Win32::TieRegistry::Registry->{'LMachine/SOFTWARE/TortoiseSVN/'}; if (!defined $tortoiseKey) { pLog("Not Found$/"); return 0; } my $tortoiseDir = $tortoiseKey->{'/Directory'}; my $subWCRevDir = File::Spec->catfile($tortoiseDir, 'bin', 'SubWCRev.exe'); if (-e $subWCRevDir) { pLog("Found$/"); $self->{'TortoiseWCRev'} = $subWCRevDir; return 1; } else { pLog("Not Found$/"); return 0; } } sub findCollabNetSVNWin32 { my $self = shift; pLog('Searching for Searching for CollabNet SVN... '); my $collabNetSVNKey = $Win32::TieRegistry::Registry->{'LMachine/SOFTWARE/CollabNet/Subversion/'}; if (!defined $collabNetSVNKey) { pLog("Not Found$/"); return 0; } my $bestCollabNetSVNVer; for my $collabNetSVNVer ($collabNetSVNKey->SubKeyNames) { $bestCollabNetSVNVer = $collabNetSVNVer if !defined $bestCollabNetSVNVer || $collabNetSVNVer >= $bestCollabNetSVNVer; } my $bestVersionKey; if (defined $bestCollabNetSVNVer) { $bestVersionKey = $collabNetSVNKey->{"$bestCollabNetSVNVer/Client/"}; if (!defined $bestVersionKey) { $bestVersionKey = $collabNetSVNKey->{"$bestCollabNetSVNVer/Server/"}; if (!defined $bestVersionKey) { pLog("Not Found$/"); $self->remember("Could find information about the location of SVN $bestCollabNetSVNVer in the registry.$/Was it installed properly?$/"); return 0; } } } else { pLog("Not Found$/"); $self->remember("Could not calculate the most recent avialable version of CollabNet SVN.$/Was it installed properly?$/"); return 0; } my $collabNetSVNDir = $bestVersionKey->{'/Install Location'}; if (-d $collabNetSVNDir) { my $collabNetSVNExe = File::Spec->catfile($collabNetSVNDir, 'bin', 'svn.exe'); if (-e $collabNetSVNExe) { $self->{'SVNExe'} = $collabNetSVNExe; pLog("Found$/"); return 1; } $collabNetSVNExe = File::Spec->catfile($collabNetSVNDir, 'svn.exe'); if (-e $collabNetSVNExe) { $self->{'SVNExe'} = $collabNetSVNExe; pLog("Found$/"); return 1; } pLog("Not Found$/"); $self->remember("Could not get a valid CollabNet SVN install location from the registry.$/Was it installed properly?$/"); return 0; } else { pLog("Not Found$/"); $self->remember("Could not get a valid CollabNet SVN install location from the registry.$/Was it installed properly?$/"); return 0; } } sub findTigrisSVNWin32 { my $self = shift; pLog('Searching for Tigris SVN... '); my $tigrisSVNKey = $Win32::TieRegistry::Registry->{'CUser/SOFTWARE/Microsoft/Windows/CurrentVersion/App Paths/svn.exe/'}; if (!defined $tigrisSVNKey) { pLog("Not Found$/"); return 0; } my $tigrisSVNExe = $tigrisSVNKey->{'/'}; if (-e $tigrisSVNExe) { $self->{'SVNExe'} = $tigrisSVNExe; pLog("Found$/"); return 1; } else { pLog("Not Found$/"); $self->remember("Could not get a valid Tigris SVN executable install location from the registry.$/Was it installed properly?$/"); return 0; } } sub findSlikSVNWin32 { my $self = shift; pLog('Searching for Slik SVN... '); my $slikSVNKey = $Win32::TieRegistry::Registry->{'LMachine/SOFTWARE/SlikSVN/Install/'}; if (!defined $slikSVNKey) { pLog("Not Found$/"); return 0; } my $slikDir = $slikSVNKey->{'/Location'}; my $slikSVNExe = File::Spec->catfile($slikDir, 'svn.exe'); if (-e $slikSVNExe) { $self->{'SVNExe'} = $slikSVNExe; pLog("Found$/"); return 1; } else { pLog("Not Found$/"); $self->remember("Could not get a valid Slik SVN install location from the registry.$/Was it installed properly?$/"); return 0; } } sub findBuildSystem { my $self = shift; if ($self->{'platform'} eq $self->PLATFORM_WIN32) { return $self->findMSDev(@_); } elsif ($self->{'platform'} eq OlympusBuild::PLATFORM_MACOSX) { return $self->findXCodeBuild(); } else { my $returnVal = $self->findMake; $returnVal = $returnVal && $self->findGCC; return $returnVal; } } sub findMSDev { my $self = shift; my %params = @_; if (defined $params{'MSDevVer'}) { pLog("Searching for Microsoft Visual Studio $params{'MSDevVer'}... "); my $keyVer = ($params{'MSDevVer'} eq '2005'? '8.0': '9.0'); my $vcKey = $Win32::TieRegistry::Registry->{"LMachine/Software/Microsoft/VisualStudio/$keyVer/Setup/VC/"}; my $vchome = $vcKey->{'/ProductDir'}; $self->{'BuildSysExe'} = File::Spec->catfile($vchome, 'VCPackages', 'vcbuild.exe'); if (-e $self->{'BuildSysExe'}) { pLog("Found$/"); return 1; } else { pLog("Not Found$/"); $self->remember("Please install Microsoft Visual Studio $self->{'MSDevVer'}$/"); return 0; } } else { pLog('Searching for minimum valid Visual Studio version... '); my $vcKey = $Win32::TieRegistry::Registry->{'LMachine/Software/Microsoft/VisualStudio/8.0/Setup/VC/'}; my $vchome = $vcKey->{'/ProductDir'}; $self->{'BuildSysExe'} = File::Spec->catfile($vchome, 'VCPackages', 'vcbuild.exe'); if (-e $self->{'BuildSysExe'}) { $self->{'BuildSysVer'} = 2005; pLog("Visual Studio 2005 Found$/"); return 1; } else { # Check for Visual Studio 9.0 (2008) it can't find 8.0 (2005) my $vcKey = $Win32::TieRegistry::Registry->{'LMachine/Software/Microsoft/VisualStudio/9.0/Setup/VC/'}; my $vchome = $vcKey->{'/ProductDir'}; $self->{'BuildSysExe'} = File::Spec->catfile($vchome, 'VCPackages', 'vcbuild.exe'); if (-e $self->{'BuildSysExe'}) { $self->{'BuildSysVer'} = 2008; pLog("Visual Studio 2008 Found$/"); return 1; } else { pLog("Not Found$/"); $self->remember("Please install Microsoft Visual Studio 2005 or 2008.$/"); return 0; } } } return 1; } sub findXCodeBuild { pLog('Searching for xcodebuild... Not found'); return 0; } sub findMake { pLog('Searching for Make.. Not found'); return 0; } sub findGCC { pLog('Searching for GCC.. Not found'); return 0; } sub findJava { my $self = shift; if (defined $ENV{'JAVA_HOME'}) { if (-e File::Spec->catfile($ENV{'JAVA_HOME'}, 'bin', "javac$self->{'exeExentsion'}")) { pLog("Searching for Sun Java JDK... Found$/"); return 1; } } if ($self->{'platform'} eq $self->PLATFORM_WIN32) { return $self->findJavaWin32(); } } sub findJavaWin32 { my $self = shift; pLog('Searching for Sun Java JDK... '); my $javasoftKey = $Win32::TieRegistry::Registry->{'LMachine/SOFTWARE/JavaSoft/Java Development Kit/'}; if (!defined $javasoftKey) { pLog("Not Found$/"); $self->remember("Sun Java JDK not found.$/Install it from http://java.sun.com$/$/"); return 0; } my $currentVersion = $javasoftKey->{'/CurrentVersion'}; if (!defined $currentVersion) { pLog("Not Found$/"); $self->remember("No current version of Java avialable.$/Install it from http://java.sun.com$/$/"); return 0; } my $currentJavaKey = $Win32::TieRegistry::Registry->{"LMachine/SOFTWARE/JavaSoft/Java Development Kit/$currentVersion"}; if (!defined $currentJavaKey) { pLog("Not Found$/"); $self->remember("Sun Java JDK $currentVersion is not installed.$/Install it from http://java.sun.com$/$/"); return 0; } my $javaHome = $currentJavaKey->{'/JavaHome'}; if (-e File::Spec->catfile($javaHome, 'bin', 'javac.exe')) { pLog("Found JDK $currentVersion$/"); $self->setEnv('JAVA_HOME', $javaHome); $self->{'JavaHome'} = $javaHome; return 1; } else { pLog("Not Found$/"); $self->remember("javac.exe does not exist in $javaHome/bin$/Install it from http://java.sun.com$/$/"); } } sub findAnt { my $self = shift; if (defined $ENV{'ANT_HOME'}) { $self->{'Ant'} = File::Spec->catfile($ENV{'ANT_HOME'}, 'bin', 'ant$self->{"shellScriptExentsion"}'); if (-e $self->{'Ant'}) { pLog("Searching for Apache Ant... Found$/"); return 1; } } if ($self->{'platform'} eq $self->PLATFORM_WIN32) { return $self->findNetbeansAntWin32(); } elsif ($self->{'platform'} = $self->PLATFORM_MACOSX) { } } sub findNetbeansAntWin32 { my $self = shift; pLog('Searching for NetBeans\' Apache Ant... '); my $uninstallKey = $Win32::TieRegistry::Registry->{'LMachine/SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall/'}; if (!defined $uninstallKey) { pLog("Not Found$/"); $self->remember("Cannot find NetBeans.$/Please install it from http://netbeans.com.$/$/"); return 0; } my $bestNetbeansVer; for my $netbeans (grep(/nbi-nb-base-/, $uninstallKey->SubKeyNames)) { $netbeans =~ s/nbi-nb-base-//; $bestNetbeansVer = $netbeans if !defined $bestNetbeansVer || $netbeans >= $bestNetbeansVer; } my $majorNbVer; my $minorNbVer; if($bestNetbeansVer =~ /(\d+)\.(\d+)/) { $majorNbVer = $1; $minorNbVer = $2; } else { pLog("Error$/"); $self->remember("Can't determine NetBeans version for $bestNetbeansVer$/$/"); return 0; } if($bestNetbeansVer < 6) { pLog("Invalid Version$/"); $self->remember("You should install Netbeans version 6 or greater$/"); return 0; } my $bestNetbeansKey = $uninstallKey->{"nbi-nb-base-$bestNetbeansVer/"}; if (!defined $bestNetbeansKey) { pLog("Error$/"); $self->remember("Can't seem to find ant. See http://wiki.speech.cs.cmu.edu/olympus/index.php/Apache_ANT$/$/"); return 0; } my $nbhome = $bestNetbeansKey->{'/InstallLocation'}; if($majorNbVer == 6 && $minorNbVer < 1) { $self->{'Ant'} = File::Spec->catfile($nbhome, 'java1', 'ant', 'bin', "ant$self->{'shellScriptExentsion'}"); pLog("Found$/"); } else { $self->{'Ant'} = File::Spec->catfile($nbhome, 'java2', 'ant', 'bin', "ant$self->{'shellScriptExentsion'}"); pLog("Found$/"); } if (!-e $self->{'Ant'}) { pLog($self->{'Ant'}); $self->remember("Can't seem to find ant. See http://wiki.speech.cs.cmu.edu/olympus/index.php/Apache_ANT$/$/"); return 0; } return 1; } sub findSolution { my $self = shift; my $solution = (defined $self->{'PROJECT'}? File::Spec->catfile($self->{'PROJECT_ROOT'}, 'System.sln'): File::Spec->catfile($self->{'OLYMPUS_ROOT'}, 'Olympus.sln')); return $solution; } ############################## ## getSolution, getAnt, and getMSDev for compatibility with old build scripts sub getSolution { my $self = shift; return $self->findSolution; } sub getAnt { my $self = shift; if (!defined $self->{'Ant'}) { $self->findAnt; } return $self->{'Ant'}; } sub getMSDev { my $self = shift; if (!defined $self->{'BuildSysExe'}) { $self->findMSDev; } return $self->{'BuildSysExe'}; } ########################################## sub runProgram { my $self = shift; return $self->runWin32Prog(@_); } #Run a win32 program, logging standard error and standard out to pLog #Takes the command line of the program as an argument #Takes an optional filter as an argument. #The filter is a function applied to each line of output. #Returns the exit status of the program sub runWin32Prog { my $self = shift; my $strCommand = shift; my $filter_r = shift; pLog("Running command: $strCommand$/$/"); my $ppid = IPC::Open3::open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, "$strCommand"); if(fork()) { while() { $self->$filter_r($_) if defined $filter_r; pLog $_; } } else { while() { $self->$filter_r($_) if defined $filter_r; pLog $_;} $self->{'NO_CLEANUP'} = 1; exit 0; } waitpid $ppid, 0; return $? >> 8; } #Filter that remembers build problems. sub VSProjectFilter { my $self = shift; my $line = shift; if($line =~ /^\d+>([^\-]+)\- (\d+) error\(s\), (\d+) warning\(s\)/ && $2) { $self->remember("$1 has $2 errors$/"); } } sub setEnv { my $self = shift; if ($self->{'platform'} eq $self->PLATFORM_WIN32) { $self->setWin32Env(@_); } } sub broadcastEnvChange { my $self = shift; if ($self->{'platform'} eq $self->PLATFORM_WIN32) { $self->broadcastEnvChangeWin32; } } sub broadcastEnvChangeWin32 { my $self = shift; if (defined $self->{'BROADCAST_CHANGE'}) { new Win32::API('user32', 'SendMessage', 'NNNP', 'N')->Call(OlympusBuild::HWND_BROADCAST, OlympusBuild::WM_SETTINGCHANGE, 0, 'Environment') or pLog("ERROR: Could not notify Windows about an environoment variable change.$/You must restart your computer before building an Olympus System.$/$/"); $self->pause; print("System environment variables changed.$/You must restart your command prompt to use them.$/") } else { $self->pause; } } sub pause { my $self = shift; if (!$self->{'NO_INTERACTION'}) { print "Press ENTER to exit. "; ; } } sub setWin32Env { my $self = shift; my ($envVar, $envVal) = @_; my $envKey = $Win32::TieRegistry::Registry->{'LMachine/SYSTEM/CurrentControlSet/Control/Session Manager/Environment/'}; return if $envKey->{'/'.$envVar} eq $envVal; $envKey->{'/'.$envVar} = $envVal; $ENV{"$envVar"} = $envVal; $self->{'BROADCAST_CHANGE'} = 1; } 1;