BuildSrc.pm 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498
  1. # ------------------------------------------------------------------------------
  2. # NAME
  3. # Fcm::BuildSrc
  4. #
  5. # DESCRIPTION
  6. # This is a class to group functionalities of source in a build.
  7. #
  8. # COPYRIGHT
  9. # (C) Crown copyright Met Office. All rights reserved.
  10. # For further details please refer to the file COPYRIGHT.txt
  11. # which you should have received as part of this distribution.
  12. # ------------------------------------------------------------------------------
  13. use strict;
  14. use warnings;
  15. package Fcm::BuildSrc;
  16. use base qw{Fcm::Base};
  17. use Carp qw{croak};
  18. use Cwd qw{cwd};
  19. use Fcm::Build::Fortran;
  20. use Fcm::CfgFile;
  21. use Fcm::CfgLine;
  22. use Fcm::Config;
  23. use Fcm::Timer qw{timestamp_command};
  24. use Fcm::Util qw{find_file_in_path run_command};
  25. use File::Basename qw{basename dirname};
  26. use File::Spec;
  27. # List of scalar property methods for this class
  28. my @scalar_properties = (
  29. 'children', # list of children packages
  30. 'is_updated', # is this source (or its associated settings) updated?
  31. 'mtime', # modification time of src
  32. 'ppmtime', # modification time of ppsrc
  33. 'ppsrc', # full path of the pre-processed source
  34. 'pkgname', # package name of the source
  35. 'progname', # program unit name in the source
  36. 'src', # full path of the source
  37. 'type', # type of the source
  38. );
  39. # List of hash property methods for this class
  40. my @hash_properties = (
  41. 'dep', # dependencies
  42. 'ppdep', # pre-process dependencies
  43. 'rules', # make rules
  44. );
  45. # Error message formats
  46. my %ERR_MESS_OF = (
  47. CHDIR => '%s: cannot change directory (%s), abort',
  48. OPEN => '%s: cannot open (%s), abort',
  49. CLOSE_PIPE => '%s: failed (%d), abort',
  50. );
  51. # Event message formats and levels
  52. my %EVENT_SETTING_OF = (
  53. CHDIR => ['%s: change directory' , 2],
  54. F_INTERFACE_NONE => ['%s: Fortran interface generation is off', 3],
  55. GET_DEPENDENCY => ['%s: %d line(s), %d auto dependency(ies)', 3],
  56. );
  57. my %RE_OF = (
  58. F_PREFIX => qr{
  59. (?:
  60. (?:ELEMENTAL|PURE(?:\s+RECURSIVE)?|RECURSIVE(?:\s+PURE)?)
  61. \s+
  62. )?
  63. }imsx,
  64. F_SPEC => qr{
  65. (?:
  66. (?:CHARACTER|COMPLEX|DOUBLE\s*PRECISION|INTEGER|LOGICAL|REAL|TYPE)
  67. (?: \s* \( .+ \) | \s* \* \d+ \s*)??
  68. \s+
  69. )?
  70. }imsx,
  71. );
  72. {
  73. # Returns a singleton instance of Fcm::Build::Fortran.
  74. my $FORTRAN_UTIL;
  75. sub _get_fortran_util {
  76. $FORTRAN_UTIL ||= Fcm::Build::Fortran->new();
  77. return $FORTRAN_UTIL;
  78. }
  79. }
  80. # ------------------------------------------------------------------------------
  81. # SYNOPSIS
  82. # $obj = Fcm::BuildSrc->new (%args);
  83. #
  84. # DESCRIPTION
  85. # This method constructs a new instance of the Fcm::BuildSrc class. See
  86. # above for allowed list of properties. (KEYS should be in uppercase.)
  87. # ------------------------------------------------------------------------------
  88. sub new {
  89. my ($class, %args) = @_;
  90. my $self = bless(Fcm::Base->new(%args), $class);
  91. for my $key (@scalar_properties, @hash_properties) {
  92. $self->{$key}
  93. = exists($args{uc($key)}) ? $args{uc($key)}
  94. : undef
  95. ;
  96. }
  97. $self;
  98. }
  99. # ------------------------------------------------------------------------------
  100. # SYNOPSIS
  101. # $value = $obj->X;
  102. # $obj->X ($value);
  103. #
  104. # DESCRIPTION
  105. # Details of these properties are explained in @scalar_properties.
  106. # ------------------------------------------------------------------------------
  107. for my $name (@scalar_properties) {
  108. no strict 'refs';
  109. *$name = sub {
  110. my $self = shift;
  111. # Argument specified, set property to specified argument
  112. if (@_) {
  113. $self->{$name} = $_[0];
  114. if ($name eq 'ppsrc') {
  115. $self->ppmtime (undef);
  116. } elsif ($name eq 'src') {
  117. $self->mtime (undef);
  118. }
  119. }
  120. # Default value for property
  121. if (not defined $self->{$name}) {
  122. if ($name eq 'children') {
  123. # Reference to an empty array
  124. $self->{$name} = [];
  125. } elsif ($name =~ /^(?:is_cur|pkgname|ppsrc|src)$/) {
  126. # Empty string
  127. $self->{$name} = '';
  128. } elsif ($name eq 'mtime') {
  129. # Modification time
  130. $self->{$name} = (stat $self->src)[9] if $self->src;
  131. } elsif ($name eq 'ppmtime') {
  132. # Modification time
  133. $self->{$name} = (stat $self->ppsrc)[9] if $self->ppsrc;
  134. } elsif ($name eq 'type') {
  135. # Attempt to get the type if src is set
  136. $self->{$name} = $self->get_type if $self->src;
  137. }
  138. }
  139. return $self->{$name};
  140. }
  141. }
  142. # ------------------------------------------------------------------------------
  143. # SYNOPSIS
  144. # %hash = %{ $obj->X () };
  145. # $obj->X (\%hash);
  146. #
  147. # $value = $obj->X ($index);
  148. # $obj->X ($index, $value);
  149. #
  150. # DESCRIPTION
  151. # Details of these properties are explained in @hash_properties.
  152. #
  153. # If no argument is set, this method returns a hash containing a list of
  154. # objects. If an argument is set and it is a reference to a hash, the objects
  155. # are replaced by the the specified hash.
  156. #
  157. # If a scalar argument is specified, this method returns a reference to an
  158. # object, if the indexed object exists or undef if the indexed object does
  159. # not exist. If a second argument is set, the $index element of the hash will
  160. # be set to the value of the argument.
  161. # ------------------------------------------------------------------------------
  162. for my $name (@hash_properties) {
  163. no strict 'refs';
  164. *$name = sub {
  165. my ($self, $arg1, $arg2) = @_;
  166. # Ensure property is defined as a reference to a hash
  167. if (not defined $self->{$name}) {
  168. if ($name eq 'rules') {
  169. $self->{$name} = $self->get_rules;
  170. } else {
  171. $self->{$name} = {};
  172. }
  173. }
  174. # Argument 1 can be a reference to a hash or a scalar index
  175. my ($index, %hash);
  176. if (defined $arg1) {
  177. if (ref ($arg1) eq 'HASH') {
  178. %hash = %$arg1;
  179. } else {
  180. $index = $arg1;
  181. }
  182. }
  183. if (defined $index) {
  184. # A scalar index is defined, set and/or return the value of an element
  185. $self->{$name}{$index} = $arg2 if defined $arg2;
  186. return (
  187. exists $self->{$name}{$index} ? $self->{$name}{$index} : undef
  188. );
  189. } else {
  190. # A scalar index is not defined, set and/or return the hash
  191. $self->{$name} = \%hash if defined $arg1;
  192. return $self->{$name};
  193. }
  194. }
  195. }
  196. # ------------------------------------------------------------------------------
  197. # SYNOPSIS
  198. # $value = $obj->X;
  199. # $obj->X ($value);
  200. #
  201. # DESCRIPTION
  202. # This method returns/sets property X, all derived from src, where X is:
  203. # base - (read-only) basename of src
  204. # dir - (read-only) dirname of src
  205. # ext - (read-only) file extension of src
  206. # root - (read-only) basename of src without the file extension
  207. # ------------------------------------------------------------------------------
  208. sub base {
  209. return &basename ($_[0]->src);
  210. }
  211. # ------------------------------------------------------------------------------
  212. sub dir {
  213. return &dirname ($_[0]->src);
  214. }
  215. # ------------------------------------------------------------------------------
  216. sub ext {
  217. return substr $_[0]->base, length ($_[0]->root);
  218. }
  219. # ------------------------------------------------------------------------------
  220. sub root {
  221. (my $root = $_[0]->base) =~ s/\.\w+$//;
  222. return $root;
  223. }
  224. # ------------------------------------------------------------------------------
  225. # SYNOPSIS
  226. # $value = $obj->X;
  227. # $obj->X ($value);
  228. #
  229. # DESCRIPTION
  230. # This method returns/sets property X, all derived from ppsrc, where X is:
  231. # ppbase - (read-only) basename of ppsrc
  232. # ppdir - (read-only) dirname of ppsrc
  233. # ppext - (read-only) file extension of ppsrc
  234. # pproot - (read-only) basename of ppsrc without the file extension
  235. # ------------------------------------------------------------------------------
  236. sub ppbase {
  237. return &basename ($_[0]->ppsrc);
  238. }
  239. # ------------------------------------------------------------------------------
  240. sub ppdir {
  241. return &dirname ($_[0]->ppsrc);
  242. }
  243. # ------------------------------------------------------------------------------
  244. sub ppext {
  245. return substr $_[0]->ppbase, length ($_[0]->pproot);
  246. }
  247. # ------------------------------------------------------------------------------
  248. sub pproot {
  249. (my $root = $_[0]->ppbase) =~ s/\.\w+$//;
  250. return $root;
  251. }
  252. # ------------------------------------------------------------------------------
  253. # SYNOPSIS
  254. # $value = $obj->X;
  255. #
  256. # DESCRIPTION
  257. # This method returns/sets property X, derived from src or ppsrc, where X is:
  258. # curbase - (read-only) basename of cursrc
  259. # curdir - (read-only) dirname of cursrc
  260. # curext - (read-only) file extension of cursrc
  261. # curmtime - (read-only) modification time of cursrc
  262. # curroot - (read-only) basename of cursrc without the file extension
  263. # cursrc - ppsrc or src
  264. # ------------------------------------------------------------------------------
  265. for my $name (qw/base dir ext mtime root src/) {
  266. no strict 'refs';
  267. my $subname = 'cur' . $name;
  268. *$subname = sub {
  269. my $self = shift;
  270. my $method = $self->ppsrc ? 'pp' . $name : $name;
  271. return $self->$method (@_);
  272. }
  273. }
  274. # ------------------------------------------------------------------------------
  275. # SYNOPSIS
  276. # $base = $obj->X ();
  277. #
  278. # DESCRIPTION
  279. # This method returns a basename X for the source, where X is:
  280. # donebase - "done" file name
  281. # etcbase - target for copying data files
  282. # exebase - executable name for source containing a main program
  283. # interfacebase - Fortran interface file name
  284. # libbase - library file name
  285. # objbase - object name for source containing compilable source
  286. # If the source file contains a compilable procedure, this method returns
  287. # the name of the object file.
  288. # ------------------------------------------------------------------------------
  289. sub donebase {
  290. my $self = shift;
  291. my $return;
  292. if ($self->is_type_all ('SOURCE')) {
  293. if ($self->objbase and not $self->is_type_all ('PROGRAM')) {
  294. $return = ($self->progname ? $self->progname : lc ($self->curroot)) .
  295. $self->setting (qw/OUTFILE_EXT DONE/);
  296. }
  297. } elsif ($self->is_type_all ('INCLUDE')) {
  298. $return = $self->curbase . $self->setting (qw/OUTFILE_EXT IDONE/);
  299. }
  300. return $return;
  301. }
  302. # ------------------------------------------------------------------------------
  303. sub etcbase {
  304. my $self = shift;
  305. my $return = @{ $self->children }
  306. ? $self->pkgname . $self->setting (qw/OUTFILE_EXT ETC/)
  307. : undef;
  308. return $return;
  309. }
  310. # ------------------------------------------------------------------------------
  311. sub exebase {
  312. my $self = shift;
  313. my $return;
  314. if ($self->objbase and $self->is_type_all ('PROGRAM')) {
  315. if ($self->setting ('BLD_EXE_NAME', $self->curroot)) {
  316. $return = $self->setting ('BLD_EXE_NAME', $self->curroot);
  317. } else {
  318. $return = $self->curroot . $self->setting (qw/OUTFILE_EXT EXE/);
  319. }
  320. }
  321. return $return;
  322. }
  323. # ------------------------------------------------------------------------------
  324. sub interfacebase {
  325. my $self = shift();
  326. if (
  327. uc($self->get_setting(qw/TOOL GENINTERFACE/)) ne 'NONE'
  328. && $self->progname()
  329. && $self->is_type_all(qw/SOURCE/)
  330. && $self->is_type_any(qw/FORTRAN9X FPP9X/)
  331. && !$self->is_type_any(qw/PROGRAM MODULE BLOCKDATA/)
  332. ) {
  333. my $flag = lc($self->get_setting(qw/TOOL INTERFACE/));
  334. my $ext = $self->setting(qw/OUTFILE_EXT INTERFACE/);
  335. return (($flag eq 'program' ? $self->progname() : $self->curroot()) . $ext);
  336. }
  337. return;
  338. }
  339. # ------------------------------------------------------------------------------
  340. sub objbase {
  341. my $self = shift;
  342. my $return;
  343. if ($self->is_type_all ('SOURCE')) {
  344. my $ext = $self->setting (qw/OUTFILE_EXT OBJ/);
  345. if ($self->is_type_any (qw/FORTRAN FPP/)) {
  346. $return = lc ($self->progname) . $ext if $self->progname;
  347. } else {
  348. $return = lc ($self->curroot) . $ext;
  349. }
  350. }
  351. return $return;
  352. }
  353. # ------------------------------------------------------------------------------
  354. # SYNOPSIS
  355. # $value = $obj->flagsbase ($flag, [$index,]);
  356. #
  357. # DESCRIPTION
  358. # This method returns the property flagsbase (derived from pkgname) the base
  359. # name of the flags-file (to indicate changes in a particular build tool) for
  360. # $flag, which can have the value:
  361. # *FLAGS - compiler flags flags-file
  362. # *PPKEYS - pre-processor keys (i.e. macro definitions) flags-file
  363. # LD - linker flags-file
  364. # LDFLAGS - linker flags flags-file
  365. # If $index is set, the $index'th element in pkgnames is used for the package
  366. # name.
  367. # ------------------------------------------------------------------------------
  368. sub flagsbase {
  369. my ($self, $flag, $index) = @_;
  370. (my $pkg = $index ? $self->pkgnames->[$index] : $self->pkgname) =~ s/\.\w+$//;
  371. if ($self->is_type_all ('SOURCE')) {
  372. if ($flag eq 'FLAGS' or $flag eq 'PPKEYS' and $self->lang) {
  373. my %tool_src = %{ $self->setting ('TOOL_SRC') };
  374. $flag = $tool_src{$self->lang}{$flag} ? $tool_src{$self->lang}{$flag} : '';
  375. }
  376. }
  377. if ($flag) {
  378. return join ('__', ($flag, $pkg ? $pkg : ())) .
  379. $self->setting (qw/OUTFILE_EXT FLAGS/);
  380. } else {
  381. return undef;
  382. }
  383. }
  384. # ------------------------------------------------------------------------------
  385. # SYNOPSIS
  386. # $value = $obj->libbase ([$prefix], [$suffix]);
  387. #
  388. # DESCRIPTION
  389. # This method returns the property libbase (derived from pkgname) the base
  390. # name of the library archive. $prefix and $suffix defaults to 'lib' and '.a'
  391. # respectively.
  392. # ------------------------------------------------------------------------------
  393. sub libbase {
  394. my ($self, $prefix, $suffix) = @_;
  395. $prefix ||= 'lib';
  396. $suffix ||= $self->setting(qw/OUTFILE_EXT LIB/);
  397. if ($self->src()) { # applies to directories only
  398. return;
  399. }
  400. my $name = $self->setting('BLD_LIB', $self->pkgname());
  401. if (!defined($name)) {
  402. $name = $self->pkgname();
  403. }
  404. $prefix . $name . $suffix;
  405. }
  406. # ------------------------------------------------------------------------------
  407. # SYNOPSIS
  408. # $value = $obj->lang ([$setting]);
  409. #
  410. # DESCRIPTION
  411. # This method returns the property lang (derived from type) the programming
  412. # language name if type matches one supported in the TOOL_SRC setting. If
  413. # $setting is specified, use $setting instead of TOOL_SRC.
  414. # ------------------------------------------------------------------------------
  415. sub lang {
  416. my ($self, $setting) = @_;
  417. my @keys = keys %{ $self->setting ($setting ? $setting : 'TOOL_SRC') };
  418. my $return = undef;
  419. for my $key (@keys) {
  420. next unless $self->is_type_all ('SOURCE', $key);
  421. $return = $key;
  422. last;
  423. }
  424. return $return;
  425. }
  426. # ------------------------------------------------------------------------------
  427. # SYNOPSIS
  428. # $value = $obj->pkgnames;
  429. #
  430. # DESCRIPTION
  431. # This method returns a list of container packages, derived from pkgname:
  432. # ------------------------------------------------------------------------------
  433. sub pkgnames {
  434. my $self = shift;
  435. my $return = [];
  436. if ($self->pkgname) {
  437. my @names = split (/__/, $self->pkgname);
  438. for my $i (0 .. $#names) {
  439. push @$return, join ('__', (@names[0 .. $i]));
  440. }
  441. unshift @$return, '';
  442. }
  443. return $return;
  444. }
  445. # ------------------------------------------------------------------------------
  446. # SYNOPSIS
  447. # %dep = %{$obj->get_dep()};
  448. # %dep = %{$obj->get_dep($flag)};
  449. #
  450. # DESCRIPTION
  451. # This method scans the current source file for dependencies and returns the
  452. # dependency hash (keys = dependencies, values = dependency types). If $flag
  453. # is specified, the config setting for $flag is used to determine the types of
  454. # types. Otherwise, those specified in 'BLD_TYPE_DEP' is used.
  455. # ------------------------------------------------------------------------------
  456. sub get_dep {
  457. my ($self, $flag) = @_;
  458. # Work out list of exclude for this file, using its sub-package name
  459. my %EXCLUDE_SET = map {($_, 1)} @{$self->get_setting('BLD_DEP_EXCL')};
  460. # Determine what dependencies are supported by this known type
  461. my %DEP_TYPE_OF = %{$self->setting($flag ? $flag : 'BLD_TYPE_DEP')};
  462. my %PATTERN_OF = %{$self->setting('BLD_DEP_PATTERN')};
  463. my @dep_types = ();
  464. if (!$self->get_setting('BLD_DEP_N')) {
  465. DEP_TYPE:
  466. while (my ($key, $dep_type_string) = each(%DEP_TYPE_OF)) {
  467. # Check if current file is a type of file requiring dependency scan
  468. if (!$self->is_type_all($key)) {
  469. next DEP_TYPE;
  470. }
  471. # Get list of dependency type for this file
  472. for my $dep_type (split(/$Fcm::Config::DELIMITER/, $dep_type_string)) {
  473. if (exists($PATTERN_OF{$dep_type}) && !exists($EXCLUDE_SET{$dep_type})) {
  474. push(@dep_types, $dep_type);
  475. }
  476. }
  477. }
  478. }
  479. # Automatic dependencies
  480. my %dep_of;
  481. my $can_get_symbol # Also scan for program unit name in Fortran source
  482. = !$flag
  483. && $self->is_type_all('SOURCE')
  484. && $self->is_type_any(qw/FPP FORTRAN/)
  485. ;
  486. my $has_read_file;
  487. if ($can_get_symbol || @dep_types) {
  488. my $handle = _open($self->cursrc());
  489. LINE:
  490. while (my $line = readline($handle)) {
  491. chomp($line);
  492. if ($line =~ qr{\A \s* \z}msx) { # empty lines
  493. next LINE;
  494. }
  495. if ($can_get_symbol) {
  496. my $symbol = _get_dep_symbol($line);
  497. if ($symbol) {
  498. $self->progname($symbol);
  499. $can_get_symbol = 0;
  500. next LINE;
  501. }
  502. }
  503. DEP_TYPE:
  504. for my $dep_type (@dep_types) {
  505. my ($match) = $line =~ /$PATTERN_OF{$dep_type}/i;
  506. if (!$match) {
  507. next DEP_TYPE;
  508. }
  509. # $match may contain multiple items delimited by space
  510. for my $item (split(qr{\s+}msx, $match)) {
  511. my $key = uc($dep_type . $Fcm::Config::DELIMITER . $item);
  512. if (!exists($EXCLUDE_SET{$key})) {
  513. $dep_of{$item} = $dep_type;
  514. }
  515. }
  516. next LINE;
  517. }
  518. }
  519. $self->_event('GET_DEPENDENCY', $self->pkgname(), $., scalar(keys(%dep_of)));
  520. close($handle);
  521. $has_read_file = 1;
  522. }
  523. # Manual dependencies
  524. my $manual_deps_ref
  525. = $self->setting('BLD_DEP' . ($flag ? '_PP' : ''), $self->pkgname());
  526. if (defined($manual_deps_ref)) {
  527. for (@{$manual_deps_ref}) {
  528. my ($dep_type, $item) = split(/$Fcm::Config::DELIMITER/, $_, 2);
  529. $dep_of{$item} = $dep_type;
  530. }
  531. }
  532. return ($has_read_file, \%dep_of);
  533. }
  534. # Returns, if possible, the program unit declared in the $line.
  535. sub _get_dep_symbol {
  536. my $line = shift();
  537. for my $pattern (
  538. qr{\A \s* $RE_OF{F_PREFIX} SUBROUTINE \s+ ([A-Za-z]\w*)}imsx,
  539. qr{\A \s* MODULE (?!\s+PROCEDURE) \s+ ([A-Za-z]\w*)}imsx,
  540. qr{\A \s* PROGRAM \s+ ([A-Za-z]\w*)}imsx,
  541. qr{\A \s* $RE_OF{F_PREFIX} $RE_OF{F_SPEC} FUNCTION \s+ ([A-Za-z]\w*)}imsx,
  542. qr{\A \s* BLOCK\s*DATA \s+ ([A-Za-z]\w*)}imsx,
  543. ) {
  544. my ($match) = $line =~ $pattern;
  545. if ($match) {
  546. return lc($match);
  547. }
  548. }
  549. return;
  550. }
  551. # ------------------------------------------------------------------------------
  552. # SYNOPSIS
  553. # @out = @{ $obj->get_fortran_interface () };
  554. #
  555. # DESCRIPTION
  556. # This method invokes the Fortran interface block generator to generate
  557. # an interface block for the current source file. It returns a reference to
  558. # an array containing the lines of the interface block.
  559. # ------------------------------------------------------------------------------
  560. sub get_fortran_interface {
  561. my $self = shift();
  562. my %ACTION_OF = (
  563. q{} => \&_get_fortran_interface_by_internal_code,
  564. f90aib => \&_get_fortran_interface_by_f90aib,
  565. none => sub {$self->_event('F_INTERFACE_NONE', $self->root()); []},
  566. );
  567. my $key = lc($self->get_setting(qw/TOOL GENINTERFACE/));
  568. if (!$key || !exists($ACTION_OF{$key})) {
  569. $key = q{};
  570. }
  571. $ACTION_OF{$key}->($self->cursrc());
  572. }
  573. # Generates Fortran interface block using "f90aib".
  574. sub _get_fortran_interface_by_f90aib {
  575. my $path = shift();
  576. my $command = sprintf(q{f90aib <'%s' 2>'%s'}, $path, File::Spec->devnull());
  577. my $pipe = _open($command, '-|');
  578. my @lines = readline($pipe);
  579. close($pipe) || croak($ERR_MESS_OF{CLOSE_PIPE}, $command, $?);
  580. \@lines;
  581. }
  582. # Generates Fortran interface block using internal code.
  583. sub _get_fortran_interface_by_internal_code {
  584. my $path = shift();
  585. my $handle = _open($path);
  586. my @lines = _get_fortran_util()->extract_interface($handle);
  587. close($handle);
  588. \@lines;
  589. }
  590. # ------------------------------------------------------------------------------
  591. # SYNOPSIS
  592. # @out = @{ $obj->get_pre_process () };
  593. #
  594. # DESCRIPTION
  595. # This method invokes the pre-processor on the source file and returns a
  596. # reference to an array containing the lines of the pre-processed source on
  597. # success.
  598. # ------------------------------------------------------------------------------
  599. sub get_pre_process {
  600. my $self = shift;
  601. # Supported source files
  602. my $lang = $self->lang ('TOOL_SRC_PP');
  603. return unless $lang;
  604. # List of include directories
  605. my @inc = @{ $self->setting (qw/PATH INC/) };
  606. # Build the pre-processor command according to file type
  607. my %tool = %{ $self->setting ('TOOL') };
  608. my %tool_src_pp = %{ $self->setting ('TOOL_SRC_PP', $lang) };
  609. # The pre-processor command and its options
  610. my @command = ($tool{$tool_src_pp{COMMAND}});
  611. my @ppflags = split /\s+/, $self->get_setting ('TOOL', $tool_src_pp{FLAGS});
  612. # List of defined macros, add "-D" in front of each macro
  613. my @ppkeys = split /\s+/, $self->get_setting ('TOOL', $tool_src_pp{PPKEYS});
  614. @ppkeys = map {($tool{$tool_src_pp{DEFINE}} . $_)} @ppkeys;
  615. # Add "-I" in front of each include directories
  616. @inc = map {($tool{$tool_src_pp{INCLUDE}} . $_)} @inc;
  617. push @command, (@ppflags, @ppkeys, @inc, $self->base);
  618. # Change to container directory of source file
  619. my $old_cwd = $self->_chdir($self->dir());
  620. # Execute the command, getting the output lines
  621. my $verbose = $self->verbose;
  622. my @outlines = &run_command (
  623. \@command, METHOD => 'qx', PRINT => $verbose > 1, TIME => $verbose > 2,
  624. );
  625. # Change back to original directory
  626. $self->_chdir($old_cwd);
  627. return \@outlines;
  628. }
  629. # ------------------------------------------------------------------------------
  630. # SYNOPSIS
  631. # $rules = %{ $self->get_rules };
  632. #
  633. # DESCRIPTION
  634. # This method returns a reference to a hash in the following format:
  635. # $rules = {
  636. # target => {ACTION => action, DEP => [dependencies], ...},
  637. # ... => {...},
  638. # };
  639. # where the 1st rank keys are the available targets for building this source
  640. # file, the second rank keys are ACTION and DEP. The value of ACTION is the
  641. # action for building the target, which can be "COMPILE", "LOAD", "TOUCH",
  642. # "CP" or "AR". The value of DEP is a refernce to an array containing a list
  643. # of dependencies suitable for insertion into the Makefile.
  644. # ------------------------------------------------------------------------------
  645. sub get_rules {
  646. my $self = shift;
  647. my $rules;
  648. my %outfile_ext = %{ $self->setting ('OUTFILE_EXT') };
  649. if ($self->is_type_all (qw/SOURCE/)) {
  650. # Source file
  651. # --------------------------------------------------------------------------
  652. # Determine whether the language of the source file is supported
  653. my %tool_src = %{ $self->setting ('TOOL_SRC') };
  654. return () unless $self->lang;
  655. # Compile object
  656. # --------------------------------------------------------------------------
  657. if ($self->objbase) {
  658. # Depends on the source file
  659. my @dep = ($self->rule_src);
  660. # Depends on the compiler flags flags-file
  661. my @flags;
  662. push @flags, ('FLAGS' )
  663. if $self->flagsbase ('FLAGS' );
  664. push @flags, ('PPKEYS')
  665. if $self->flagsbase ('PPKEYS') and not $self->ppsrc;
  666. push @dep, $self->flagsbase ($_) for (@flags);
  667. # Source file dependencies
  668. for my $name (sort keys %{ $self->dep }) {
  669. # A Fortran 9X module, lower case object file name
  670. if ($self->dep ($name) eq 'USE') {
  671. (my $root = $name) =~ s/\.\w+$//;
  672. push @dep, lc ($root) . $outfile_ext{OBJ};
  673. # An include file
  674. } elsif ($self->dep ($name) =~ /^(?:INC|H|INTERFACE)$/) {
  675. push @dep, $name;
  676. }
  677. }
  678. $rules->{$self->objbase} = {ACTION => 'COMPILE', DEP => \@dep};
  679. # Touch flags-files
  680. # ------------------------------------------------------------------------
  681. for my $flag (@flags) {
  682. next unless $self->flagsbase ($flag);
  683. $rules->{$self->flagsbase ($flag)} = {
  684. ACTION => 'TOUCH',
  685. DEP => [
  686. $self->flagsbase ($tool_src{$self->lang}{$flag}, -2),
  687. ],
  688. DEST => '$(FCM_FLAGSDIR)',
  689. };
  690. }
  691. }
  692. if ($self->exebase) {
  693. # Link into an executable
  694. # ------------------------------------------------------------------------
  695. my @dep = ();
  696. push @dep, $self->objbase if $self->objbase;
  697. push @dep, $self->flagsbase ('LD' ) if $self->flagsbase ('LD' );
  698. push @dep, $self->flagsbase ('LDFLAGS') if $self->flagsbase ('LDFLAGS');
  699. # Depends on BLOCKDATA program units, for Fortran programs
  700. my %blockdata = %{ $self->setting ('BLD_BLOCKDATA') };
  701. my @blkobj = ();
  702. if ($self->is_type_any (qw/FPP FORTRAN/) and keys %blockdata) {
  703. # List of BLOCKDATA object files
  704. if (exists $blockdata{$self->exebase}) {
  705. @blkobj = split /\s+/, $blockdata{$self->exebase};
  706. } elsif (exists $blockdata{''}) {
  707. @blkobj = split /\s+/, $blockdata{''};
  708. }
  709. for my $name (@blkobj) {
  710. (my $root = $name) =~ s/\.\w+$//;
  711. $name = $root . $outfile_ext{OBJ};
  712. push @dep, $root . $outfile_ext{DONE};
  713. }
  714. }
  715. # Extra executable dependencies
  716. my %exe_dep = %{ $self->setting ('BLD_DEP_EXE') };
  717. if (keys %exe_dep) {
  718. my @exe_deps;
  719. if (exists $exe_dep{$self->exebase}) {
  720. @exe_deps = split /\s+/, $exe_dep{$self->exebase};
  721. } elsif (exists $exe_dep{''}) {
  722. @exe_deps = $exe_dep{''} ? split (/\s+/, $exe_dep{''}) : ('');
  723. }
  724. my $pattern = '\\' . $outfile_ext{OBJ} . '$';
  725. for my $name (@exe_deps) {
  726. if ($name =~ /$pattern/) {
  727. # Extra dependency is an object
  728. (my $root = $name) =~ s/\.\w+$//;
  729. push @dep, $root . $outfile_ext{DONE};
  730. } else {
  731. # Extra dependency is a sub-package
  732. my $var;
  733. if ($self->setting ('FCM_PCK_OBJECTS', $name)) {
  734. # sub-package name contains unusual characters
  735. $var = $self->setting ('FCM_PCK_OBJECTS', $name);
  736. } else {
  737. # sub-package name contains normal characters
  738. $var = $name ? join ('__', ('OBJECTS', $name)) : 'OBJECTS';
  739. }
  740. push @dep, '$(' . $var . ')';
  741. }
  742. }
  743. }
  744. # Source file dependencies
  745. for my $name (sort keys %{ $self->dep }) {
  746. (my $root = $name) =~ s/\.\w+$//;
  747. # Lowercase name for object dependency
  748. $root = lc ($root) unless $self->dep ($name) =~ /^(?:INC|H)$/;
  749. # Select "done" file extension
  750. if ($self->dep ($name) =~ /^(?:INC|H)$/) {
  751. push @dep, $name . $outfile_ext{IDONE};
  752. } else {
  753. push @dep, $root . $outfile_ext{DONE};
  754. }
  755. }
  756. $rules->{$self->exebase} = {
  757. ACTION => 'LOAD', DEP => \@dep, BLOCKDATA => \@blkobj,
  758. };
  759. # Touch Linker flags-file
  760. # ------------------------------------------------------------------------
  761. for my $flag (qw/LD LDFLAGS/) {
  762. $rules->{$self->flagsbase ($flag)} = {
  763. ACTION => 'TOUCH',
  764. DEP => [$self->flagsbase ($flag, -2)],
  765. DEST => '$(FCM_FLAGSDIR)',
  766. };
  767. }
  768. }
  769. if ($self->donebase) {
  770. # Touch done file
  771. # ------------------------------------------------------------------------
  772. my @dep = ($self->objbase);
  773. for my $name (sort keys %{ $self->dep }) {
  774. (my $root = $name) =~ s/\.\w+$//;
  775. # Lowercase name for object dependency
  776. $root = lc ($root) unless $self->dep ($name) =~ /^(?:INC|H)$/;
  777. # Select "done" file extension
  778. if ($self->dep ($name) =~ /^(?:INC|H)$/) {
  779. push @dep, $name . $outfile_ext{IDONE};
  780. } else {
  781. push @dep, $root . $outfile_ext{DONE};
  782. }
  783. }
  784. $rules->{$self->donebase} = {
  785. ACTION => 'TOUCH', DEP => \@dep, DEST => '$(FCM_DONEDIR)',
  786. };
  787. }
  788. if ($self->interfacebase) {
  789. # Interface target
  790. # ------------------------------------------------------------------------
  791. # Source file dependencies
  792. my @dep = ();
  793. for my $name (sort keys %{ $self->dep }) {
  794. # Depends on Fortran 9X modules
  795. push @dep, lc ($name) . $outfile_ext{OBJ}
  796. if $self->dep ($name) eq 'USE';
  797. }
  798. $rules->{$self->interfacebase} = {ACTION => '', DEP => \@dep};
  799. }
  800. } elsif ($self->is_type_all ('INCLUDE')) {
  801. # Copy include target
  802. # --------------------------------------------------------------------------
  803. my @dep = ($self->rule_src);
  804. for my $name (sort keys %{ $self->dep }) {
  805. # A Fortran 9X module, lower case object file name
  806. if ($self->dep ($name) eq 'USE') {
  807. (my $root = $name) =~ s/\.\w+$//;
  808. push @dep, lc ($root) . $outfile_ext{OBJ};
  809. # An include file
  810. } elsif ($self->dep ($name) =~ /^(?:INC|H|INTERFACE)$/) {
  811. push @dep, $name;
  812. }
  813. }
  814. $rules->{$self->curbase} = {
  815. ACTION => 'CP', DEP => \@dep, DEST => '$(FCM_INCDIR)',
  816. };
  817. # Touch IDONE file
  818. # --------------------------------------------------------------------------
  819. if ($self->donebase) {
  820. my @dep = ($self->rule_src);
  821. for my $name (sort keys %{ $self->dep }) {
  822. (my $root = $name) =~ s/\.\w+$//;
  823. # Lowercase name for object dependency
  824. $root = lc ($root) unless $self->dep ($name) =~ /^(?:INC|H)$/;
  825. # Select "done" file extension
  826. if ($self->dep ($name) =~ /^(?:INC|H)$/) {
  827. push @dep, $name . $outfile_ext{IDONE};
  828. } else {
  829. push @dep, $root . $outfile_ext{DONE};
  830. }
  831. }
  832. $rules->{$self->donebase} = {
  833. ACTION => 'TOUCH', DEP => \@dep, DEST => '$(FCM_DONEDIR)',
  834. };
  835. }
  836. } elsif ($self->is_type_any (qw/EXE SCRIPT/)) {
  837. # Copy executable file
  838. # --------------------------------------------------------------------------
  839. my @dep = ($self->rule_src);
  840. # Depends on dummy copy file, if file is an "always build type"
  841. push @dep, $self->setting (qw/BLD_CPDUMMY/)
  842. if $self->is_type_any (split (
  843. /$Fcm::Config::DELIMITER_LIST/, $self->setting ('BLD_TYPE_ALWAYS_BUILD')
  844. ));
  845. # Depends on other executable files
  846. for my $name (sort keys %{ $self->dep }) {
  847. push @dep, $name if $self->dep ($name) eq 'EXE';
  848. }
  849. $rules->{$self->curbase} = {
  850. ACTION => 'CP', DEP => \@dep, DEST => '$(FCM_BINDIR)',
  851. };
  852. } elsif (@{ $self->children }) {
  853. # Targets for top level and package flags files and dummy dependencies
  854. # --------------------------------------------------------------------------
  855. my %tool_src = %{ $self->setting ('TOOL_SRC') };
  856. my %flags_tool = (LD => '', LDFLAGS => '');
  857. for my $key (keys %tool_src) {
  858. $flags_tool{$tool_src{$key}{FLAGS}} = $tool_src{$key}{COMMAND}
  859. if exists $tool_src{$key}{FLAGS};
  860. $flags_tool{$tool_src{$key}{PPKEYS}} = ''
  861. if exists $tool_src{$key}{PPKEYS};
  862. }
  863. for my $name (sort keys %flags_tool) {
  864. my @dep = $self->pkgname eq '' ? () : $self->flagsbase ($name, -2);
  865. push @dep, $self->flagsbase ($flags_tool{$name})
  866. if $self->pkgname eq '' and $flags_tool{$name};
  867. $rules->{$self->flagsbase ($flags_tool{$name})} = {
  868. ACTION => 'TOUCH',
  869. DEST => '$(FCM_FLAGSDIR)',
  870. } if $self->pkgname eq '' and $flags_tool{$name};
  871. $rules->{$self->flagsbase ($name)} = {
  872. ACTION => 'TOUCH',
  873. DEP => \@dep,
  874. DEST => '$(FCM_FLAGSDIR)',
  875. };
  876. }
  877. # Package object and library
  878. # --------------------------------------------------------------------------
  879. {
  880. my @dep;
  881. # Add objects from children
  882. for my $child (sort {$a->pkgname cmp $b->pkgname} @{ $self->children }) {
  883. push @dep, $child->rule_obj_var (1)
  884. if $child->libbase and $child->rules ($child->libbase);
  885. push @dep, $child->objbase
  886. if $child->cursrc and $child->objbase and
  887. not $child->is_type_any (qw/PROGRAM BLOCKDATA/);
  888. }
  889. if (@dep) {
  890. $rules->{$self->libbase} = {ACTION => 'AR', DEP => \@dep};
  891. }
  892. }
  893. # Package data files
  894. # --------------------------------------------------------------------------
  895. {
  896. my @dep;
  897. for my $child (@{ $self->children }) {
  898. push @dep, $child->rule_src if $child->src and not $child->type;
  899. }
  900. if (@dep) {
  901. push @dep, $self->setting (qw/BLD_CPDUMMY/);
  902. $rules->{$self->etcbase} = {
  903. ACTION => 'CP_DATA', DEP => \@dep, DEST => '$(FCM_ETCDIR)',
  904. };
  905. }
  906. }
  907. }
  908. return $rules;
  909. }
  910. # ------------------------------------------------------------------------------
  911. # SYNOPSIS
  912. # $value = $obj->get_setting ($setting[, @prefix]);
  913. #
  914. # DESCRIPTION
  915. # This method gets the correct $setting for the current source by following
  916. # its package name. If @prefix is set, get the setting with the given prefix.
  917. # ------------------------------------------------------------------------------
  918. sub get_setting {
  919. my ($self, $setting, @prefix) = @_;
  920. my $val;
  921. for my $name (reverse @{ $self->pkgnames }) {
  922. my @names = split /__/, $name;
  923. $val = $self->setting ($setting, join ('__', (@prefix, @names)));
  924. $val = $self->setting ($setting, join ('__', (@prefix, @names)))
  925. if (not defined $val) and @names and $names[-1] =~ s/\.[^\.]+$//;
  926. last if defined $val;
  927. }
  928. return $val;
  929. }
  930. # ------------------------------------------------------------------------------
  931. # SYNOPSIS
  932. # $type = $self->get_type();
  933. #
  934. # DESCRIPTION
  935. # This method determines whether the source is a type known to the
  936. # build system. If so, it returns the type flags delimited by "::".
  937. # ------------------------------------------------------------------------------
  938. sub get_type {
  939. my $self = shift();
  940. my @IGNORE_LIST
  941. = split(/$Fcm::Config::DELIMITER_LIST/, $self->setting('INFILE_IGNORE'));
  942. if (grep {$self->curbase() eq $_} @IGNORE_LIST) {
  943. return q{};
  944. }
  945. # User defined
  946. my $type = $self->setting('BLD_TYPE', $self->pkgname());
  947. # Extension
  948. if (!defined($type)) {
  949. my $ext = $self->curext() ? substr($self->curext(), 1) : q{};
  950. $type = $self->setting('INFILE_EXT', $ext);
  951. }
  952. # Pattern of name
  953. if (!defined($type)) {
  954. my %NAME_PATTERN_TO_TYPE_HASH = %{$self->setting('INFILE_PAT')};
  955. PATTERN:
  956. while (my ($pattern, $value) = each(%NAME_PATTERN_TO_TYPE_HASH)) {
  957. if ($self->curbase() =~ $pattern) {
  958. $type = $value;
  959. last PATTERN;
  960. }
  961. }
  962. }
  963. # Pattern of #! line
  964. if (!defined($type) && -s $self->cursrc() && -T _) {
  965. my $handle = _open($self->cursrc());
  966. my $line = readline($handle);
  967. close($handle);
  968. my %SHEBANG_PATTERN_TO_TYPE_HASH = %{$self->setting('INFILE_TXT')};
  969. PATTERN:
  970. while (my ($pattern, $value) = each(%SHEBANG_PATTERN_TO_TYPE_HASH)) {
  971. if ($line =~ qr{^\#!.*$pattern}msx) {
  972. $type = $value;
  973. last PATTERN;
  974. }
  975. }
  976. }
  977. if (!$type) {
  978. return $type;
  979. }
  980. # Extra type information for selected file types
  981. my %EXTRA_FOR = (
  982. qr{\b (?:FORTRAN|FPP) \b}msx => \&_get_type_extra_for_fortran,
  983. qr{\b C \b}msx => \&_get_type_extra_for_c,
  984. );
  985. EXTRA:
  986. while (my ($key, $code_ref) = each(%EXTRA_FOR)) {
  987. if ($type =~ $key) {
  988. my $handle = _open($self->cursrc());
  989. LINE:
  990. while (my $line = readline($handle)) {
  991. my $extra = $code_ref->($line);
  992. if ($extra) {
  993. $type .= $Fcm::Config::DELIMITER . $extra;
  994. last LINE;
  995. }
  996. }
  997. close($handle);
  998. last EXTRA;
  999. }
  1000. }
  1001. return $type;
  1002. }
  1003. sub _get_type_extra_for_fortran {
  1004. my ($match) = $_[0] =~ qr{\A \s* (PROGRAM|MODULE|BLOCK\s*DATA) \b}imsx;
  1005. if (!$match) {
  1006. return;
  1007. }
  1008. $match =~ s{\s}{}g;
  1009. uc($match)
  1010. }
  1011. sub _get_type_extra_for_c {
  1012. ($_[0] =~ qr{int\s+main\s*\(}msx) ? 'PROGRAM' : undef;
  1013. }
  1014. # ------------------------------------------------------------------------------
  1015. # SYNOPSIS
  1016. # $flag = $obj->is_in_package ($name);
  1017. #
  1018. # DESCRIPTION
  1019. # This method returns true if current package is in the package $name.
  1020. # ------------------------------------------------------------------------------
  1021. sub is_in_package {
  1022. my ($self, $name) = @_;
  1023. my $return = 0;
  1024. for (@{ $self->pkgnames }) {
  1025. next unless /^$name(?:\.\w+)?$/;
  1026. $return = 1;
  1027. last;
  1028. }
  1029. return $return;
  1030. }
  1031. # ------------------------------------------------------------------------------
  1032. # SYNOPSIS
  1033. # $flag = $obj->is_type_all ($arg, ...);
  1034. # $flag = $obj->is_type_any ($arg, ...);
  1035. #
  1036. # DESCRIPTION
  1037. # This method returns a flag for the following:
  1038. # is_type_all - does type match all of the arguments?
  1039. # is_type_any - does type match any of the arguments?
  1040. # ------------------------------------------------------------------------------
  1041. for my $name ('all', 'any') {
  1042. no strict 'refs';
  1043. my $subname = 'is_type_' . $name;
  1044. *$subname = sub {
  1045. my ($self, @intypes) = @_;
  1046. my $rc = 0;
  1047. if ($self->type) {
  1048. my %types = map {($_, 1)} split /$Fcm::Config::DELIMITER/, $self->type;
  1049. for my $intype (@intypes) {
  1050. $rc = exists $types{$intype};
  1051. last if ($name eq 'all' and not $rc) or ($name eq 'any' and $rc);
  1052. }
  1053. }
  1054. return $rc;
  1055. }
  1056. }
  1057. # ------------------------------------------------------------------------------
  1058. # SYNOPSIS
  1059. # $string = $obj->rule_obj_var ([$read]);
  1060. #
  1061. # DESCRIPTION
  1062. # This method returns a string containing the make rule object variable for
  1063. # the current package. If $read is set, return $($string)
  1064. # ------------------------------------------------------------------------------
  1065. sub rule_obj_var {
  1066. my ($self, $read) = @_;
  1067. my $return;
  1068. if ($self->setting ('FCM_PCK_OBJECTS', $self->pkgname)) {
  1069. # Package name registered in unusual list
  1070. $return = $self->setting ('FCM_PCK_OBJECTS', $self->pkgname);
  1071. } else {
  1072. # Package name not registered in unusual list
  1073. $return = $self->pkgname
  1074. ? join ('__', ('OBJECTS', $self->pkgname)) : 'OBJECTS';
  1075. }
  1076. $return = $read ? '$(' . $return . ')' : $return;
  1077. return $return;
  1078. }
  1079. # ------------------------------------------------------------------------------
  1080. # SYNOPSIS
  1081. # $string = $obj->rule_src ();
  1082. #
  1083. # DESCRIPTION
  1084. # This method returns a string containing the location of the source file
  1085. # relative to the build root. This string will be suitable for use in a
  1086. # "Make" rule file for FCM.
  1087. # ------------------------------------------------------------------------------
  1088. sub rule_src {
  1089. my $self = shift;
  1090. my $return = $self->cursrc;
  1091. LABEL: for my $name (qw/SRC PPSRC/) {
  1092. for my $i (0 .. @{ $self->setting ('PATH', $name) } - 1) {
  1093. my $dir = $self->setting ('PATH', $name)->[$i];
  1094. next unless index ($self->cursrc, $dir) == 0;
  1095. $return = File::Spec->catfile (
  1096. '$(FCM_' . $name . 'DIR' . ($i ? $i : '') . ')',
  1097. File::Spec->abs2rel ($self->cursrc, $dir),
  1098. );
  1099. last LABEL;
  1100. }
  1101. }
  1102. return $return;
  1103. }
  1104. # ------------------------------------------------------------------------------
  1105. # SYNOPSIS
  1106. # $rc = $obj->write_lib_dep_excl ();
  1107. #
  1108. # DESCRIPTION
  1109. # This method writes a set of exclude dependency configurations for the
  1110. # library of this package.
  1111. # ------------------------------------------------------------------------------
  1112. sub write_lib_dep_excl {
  1113. my $self = shift();
  1114. if (!find_file_in_path($self->libbase(), $self->setting(qw/PATH LIB/))) {
  1115. return 0;
  1116. }
  1117. my $ETC_DIR = $self->setting(qw/PATH ETC/)->[0];
  1118. my $CFG_EXT = $self->setting(qw/OUTFILE_EXT CFG/);
  1119. my $LABEL_OF_EXCL_DEP = $self->cfglabel('BLD_DEP_EXCL');
  1120. my @SETTINGS = (
  1121. #dependency #source file type list #dependency name function
  1122. ['H' , [qw{INCLUDE CPP }], sub {$_[0]->base()} ],
  1123. ['INTERFACE', [qw{INCLUDE INTERFACE }], sub {$_[0]->base()} ],
  1124. ['INC' , [qw{INCLUDE }], sub {$_[0]->base()} ],
  1125. ['USE' , [qw{SOURCE FORTRAN MODULE}], sub {$_[0]->root()} ],
  1126. ['INTERFACE', [qw{SOURCE FORTRAN }], sub {$_[0]->interfacebase()}],
  1127. ['OBJ' , [qw{SOURCE }], sub {$_[0]->root()} ],
  1128. );
  1129. my $cfg = Fcm::CfgFile->new();
  1130. my @stack = ($self);
  1131. NODE:
  1132. while (my $node = pop(@stack)) {
  1133. # Is a directory
  1134. if (@{$node->children()}) {
  1135. push(@stack, reverse(@{$node->children()}));
  1136. next NODE;
  1137. }
  1138. # Is a typed file
  1139. if (
  1140. $node->cursrc()
  1141. && $node->type()
  1142. && !$node->is_type_any(qw{PROGRAM BLOCKDATA})
  1143. ) {
  1144. for (@SETTINGS) {
  1145. my ($key, $type_list_ref, $name_func_ref) = @{$_};
  1146. my $name = $name_func_ref->($node);
  1147. if ($name && $node->is_type_all(@{$type_list_ref})) {
  1148. push(
  1149. @{$cfg->lines()},
  1150. Fcm::CfgLine->new(
  1151. label => $LABEL_OF_EXCL_DEP,
  1152. value => $key . $Fcm::Config::DELIMITER . $name,
  1153. ),
  1154. );
  1155. next NODE;
  1156. }
  1157. }
  1158. }
  1159. }
  1160. # Write to configuration file
  1161. $cfg->print_cfg(
  1162. File::Spec->catfile($ETC_DIR, $self->libbase('lib', $CFG_EXT)),
  1163. );
  1164. }
  1165. # ------------------------------------------------------------------------------
  1166. # SYNOPSIS
  1167. # $string = $obj->write_rules ();
  1168. #
  1169. # DESCRIPTION
  1170. # This method returns a string containing the "Make" rules for building the
  1171. # source file.
  1172. # ------------------------------------------------------------------------------
  1173. sub write_rules {
  1174. my $self = shift;
  1175. my $mk = '';
  1176. for my $target (sort keys %{ $self->rules }) {
  1177. my $rule = $self->rules ($target);
  1178. next unless defined ($rule->{ACTION});
  1179. if ($rule->{ACTION} eq 'AR') {
  1180. my $var = $self->rule_obj_var;
  1181. $mk .= ($var eq 'OBJECTS' ? 'export ' : '') . $var . ' =';
  1182. $mk .= ' ' . join (' ', @{ $rule->{DEP} });
  1183. $mk .= "\n\n";
  1184. }
  1185. $mk .= $target . ':';
  1186. if ($rule->{ACTION} eq 'AR') {
  1187. $mk .= ' ' . $self->rule_obj_var (1);
  1188. } else {
  1189. for my $dep (@{ $rule->{DEP} }) {
  1190. $mk .= ' ' . $dep;
  1191. }
  1192. }
  1193. $mk .= "\n";
  1194. if (exists $rule->{ACTION}) {
  1195. if ($rule->{ACTION} eq 'AR') {
  1196. $mk .= "\t" . 'fcm_internal archive $@ $^' . "\n";
  1197. } elsif ($rule->{ACTION} eq 'CP') {
  1198. $mk .= "\t" . 'cp $< ' . $rule->{DEST} . "\n";
  1199. $mk .= "\t" . 'chmod u+w ' .
  1200. File::Spec->catfile ($rule->{DEST}, '$@') . "\n";
  1201. } elsif ($rule->{ACTION} eq 'CP_DATA') {
  1202. $mk .= "\t" . 'cp $^ ' . $rule->{DEST} . "\n";
  1203. $mk .= "\t" . 'touch ' .
  1204. File::Spec->catfile ($rule->{DEST}, '$@') . "\n";
  1205. } elsif ($rule->{ACTION} eq 'COMPILE') {
  1206. if ($self->lang) {
  1207. $mk .= "\t" . 'fcm_internal compile:' . substr ($self->lang, 0, 1) .
  1208. ' ' . $self->pkgnames->[-2] . ' $< $@';
  1209. $mk .= ' 1' if ($self->flagsbase ('PPKEYS') and not $self->ppsrc);
  1210. $mk .= "\n";
  1211. }
  1212. } elsif ($rule->{ACTION} eq 'LOAD') {
  1213. if ($self->lang) {
  1214. $mk .= "\t" . 'fcm_internal load:' . substr ($self->lang, 0, 1) .
  1215. ' ' . $self->pkgnames->[-2] . ' $< $@';
  1216. $mk .= ' ' . join (' ', @{ $rule->{BLOCKDATA} })
  1217. if @{ $rule->{BLOCKDATA} };
  1218. $mk .= "\n";
  1219. }
  1220. } elsif ($rule->{ACTION} eq 'TOUCH') {
  1221. $mk .= "\t" . 'touch ' .
  1222. File::Spec->catfile ($rule->{DEST}, '$@') . "\n";
  1223. }
  1224. }
  1225. $mk .= "\n";
  1226. }
  1227. return $mk;
  1228. }
  1229. # Wraps "chdir". Returns old directory.
  1230. sub _chdir {
  1231. my ($self, $dir) = @_;
  1232. my $old_cwd = cwd();
  1233. $self->_event('CHDIR', $dir);
  1234. chdir($dir) || croak(sprintf($ERR_MESS_OF{CHDIR}, $dir));
  1235. $old_cwd;
  1236. }
  1237. # Wraps an event.
  1238. sub _event {
  1239. my ($self, $key, @args) = @_;
  1240. my ($format, $level) = @{$EVENT_SETTING_OF{$key}};
  1241. $level ||= 1;
  1242. if ($self->verbose() >= $level) {
  1243. printf($format . ".\n", @args);
  1244. }
  1245. }
  1246. # Wraps "open".
  1247. sub _open {
  1248. my ($path, $mode) = @_;
  1249. $mode ||= '<';
  1250. open(my $handle, $mode, $path) || croak(sprintf($ERR_MESS_OF{OPEN}, $path, $!));
  1251. $handle;
  1252. }
  1253. # ------------------------------------------------------------------------------
  1254. 1;
  1255. __END__