diff --git a/.appveyor.yml b/.appveyor.yml index e9463cb..6fe5ea7 100755 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,8 +15,8 @@ image: build_script: # Grab the latest Data::Hopen - - svn export --force https://github.com/hopenbuild/Data-Hopen/trunk/lib - # Thanks to https://stackoverflow.com/a/39317180/2877364 + - cpanm https://github.com/hopenbuild/Data-Hopen/tarball/master + # Thanks to https://gist.github.com/keisatou/7499464 # Grab the rest of the dependencies - cpanm --installdeps --notest --verbose . diff --git a/.gitignore b/.gitignore index d1b7b9f..66bc7f6 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ t/TEMPLATE-dot-t foo foo.* TODO +TODO* # Things we generate built/ diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..00a2c8d --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ +Christopher White +Christopher White diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..da6aa61 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: perl +perl: + - "5.26" + - "5.26-shrplib" + - "5.20" + - "5.18-shrplib" + - "5.14" + # 5.14 is the minimum Data-Hopen supports + +install: + - cpanm ExtUtils::MakeMaker + # Because EUMM v6.66 doesn't seem to generate the correct MYMETA.json, + # causing lib::relative not to be loaded as a dependency. See, e.g., + # https://travis-ci.org/cxw42/Class-Tiny-ConstrainedAccessor/jobs/535823908 + + # Grab the latest Data::Hopen + - cpanm https://github.com/hopenbuild/Data-Hopen/tarball/master + # Thanks to https://gist.github.com/keisatou/7499464 + + # Grab the rest. PERL5LIB=lib + --skip-installed => skip trying to + # grab Data::Hopen, so that we can just use the latest. + - cpanm --with-recommends --verbose --installdeps --notest . diff --git a/Changes b/Changes index 680582c..2504f05 100644 --- a/Changes +++ b/Changes @@ -1,20 +1,25 @@ Revision history for App-hopen +0.000011 2019-06-04 + - Added Ninja generator + - Added examples in the `eg/` directory + - Updated documentation + 0.000010 2019-05-26 - First non-trial release. - Major internal changes. Finished removing Data-Hopen leftovers. + - First non-trial release. + - Major internal changes. Finished removing Data-Hopen leftovers. 0.000009 2019-02-24 (TRIAL RELEASE) - Split former Build-Hopen into App-hopen and Data-Hopen + - Split former Build-Hopen into App-hopen and Data-Hopen 0.000008 2019-02-09 (TRIAL RELEASE) - Tweak File::Glob usage to support Perl 5.14 + - Tweak File::Glob usage to support Perl 5.14 0.000007 2019-02-08 (TRIAL RELEASE) - First version that can generate a Makefile + - First version that can generate a Makefile 0.000006 2019-02-06 (TRIAL RELEASE) - Substantially expanded + - Substantially expanded 0.000003 2018-01-02 (TRIAL RELEASE) - First full trial release + - First full trial release diff --git a/MANIFEST b/MANIFEST index c08d538..d56dc06 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,5 +1,16 @@ bin/hopen Changes +eg/001-single-file-hello/001.hopen.pl +eg/001-single-file-hello/hello.c +eg/001-single-file-hello/README.md +eg/002-double-file-hello/.hopen.pl +eg/002-double-file-hello/hello.c +eg/002-double-file-hello/printmsg.c +eg/002-double-file-hello/README.md +eg/003-double-file-double-compile/.hopen.pl +eg/003-double-file-double-compile/hello.c +eg/003-double-file-double-compile/printmsg.c +eg/003-double-file-double-compile/README.md lib/App/hopen.pm lib/App/hopen/AppUtil.pm lib/App/hopen/Asset.pm @@ -12,6 +23,9 @@ lib/App/hopen/Gen.pm lib/App/hopen/Gen/Make.pm lib/App/hopen/Gen/Make/AssetGraphNode.pm lib/App/hopen/Gen/Make/AssetGraphVisitor.pm +lib/App/hopen/Gen/Ninja.pm +lib/App/hopen/Gen/Ninja/AssetGraphNode.pm +lib/App/hopen/Gen/Ninja/AssetGraphVisitor.pm lib/App/hopen/H.pm lib/App/hopen/HopenFileKit.pm lib/App/hopen/Phase.pod @@ -35,18 +49,17 @@ support/readme.pl t/00-load-ah.t t/005-op.t t/006-collect-op.t -t/020-dag.t t/030-util-basedpath.t t/040-ah-g-assetop.t t/100-h.t t/200-apputil-basic.t +t/900-eg-001.t t/dir200/inner.hopen.pl t/dir200/inner/.hopen.pl t/dir200/inner/z.hopen.pl t/lib/HopenTest.pm -t/samples/01/01.hopen.pl -t/samples/01/hello.c xt/boilerplate.t +xt/kwalitee.t xt/manifest.t xt/pod-coverage.t xt/pod.t diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index c834732..1d2371e 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -9,6 +9,7 @@ App-hopen-.* TODO \brunhopen$ \.stackdump$ +\.mailmap$ # Backup files \.swp$ diff --git a/Makefile.PL b/Makefile.PL index d2b6ce2..bd898d0 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -106,7 +106,7 @@ testhere: # Run the tests from lib rather than blib \tprove -lj4 README.md: @{[$VERSION_FROM]} Makefile.PL $make_readme_md -\t"$secure_perl_path" "$make_readme_md" -i "\$<" -o "\$@" -f md --appveyor cxw42/app-hopen --avbadge hopenbuild/app-hopen +\t"$secure_perl_path" "$make_readme_md" -i "\$<" -o "\$@" -f md --appveyor cxw42/app-hopen --travis hopenbuild/app-hopen README: @{[$VERSION_FROM]} Makefile.PL $make_readme_md \t"$secure_perl_path" "$make_readme_md" -i "\$<" -o "\$@" -f text @@ -143,7 +143,7 @@ EOT # Main options for EUMM my %opts = ( NAME => 'App::hopen', - AUTHOR => q{Christopher White }, + AUTHOR => q{Christopher White }, VERSION_FROM => $VERSION_FROM, ABSTRACT_FROM => $VERSION_FROM, #ABSTRACT => 'The hopen build system, command-line interface', @@ -184,7 +184,7 @@ my %opts = ( 'rlib' => '0', 'Scalar::Util' => '0', 'Test::Deep' => '0.084', # for superhashof - ## 'Test::Directory' => '0.02', # for subdirs + 'Test::Directory' => '0.02', # for subdirs 'Test::Exception' => '0', 'Test::More' => '0', # Test::TempDir::Tiny? If so, remove Test::Directory dependency? @@ -192,7 +192,7 @@ my %opts = ( }, PREREQ_PM => { # Other hopen packages ------------------- - 'Data::Hopen' => '0.000013', + 'Data::Hopen' => '0.000014', # Non-hopen dependencies ----------------- #'Algorithm::Dependency' => '1.106', @@ -215,7 +215,7 @@ my %opts = ( 'Cwd' => '0', 'constant' => '0', - 'Data::Dumper' => '0', + 'Data::Dumper' => '2.154', # For Maxrecurse 'Deep::Hash::Utils' => '0.03', # For correct metadata ## 'experimental' => '0.009', # For support on perl < 5.15.7 'Exporter' => '0', @@ -300,6 +300,7 @@ my %opts = ( requires => { 'App::RewriteVersion' => '0', # for perl-bump-version 'Module::Metadata' => '1.000016', + 'Test::Kwalitee' => '0', }, }, }, diff --git a/README b/README index 7bc8a65..44513c5 100644 --- a/README +++ b/README @@ -22,14 +22,38 @@ NAME Why Perl? Because (1) you probably already have it installed, and (2) it is the original write-once, run-everywhere language! -USAGE + Example + Create a file ".hopen.pl" in your source tree. Then: + + $ hopen + From ``.'' into ``built'' + Running Check phase + + Now "built/MY.hopen.pl" has been created, and loaded with information + about your configuration. You can edit that file if you want to change + what will happen next. + + $ hopen + From ``.'' into ``built'' + Running Gen phase + + Now "built/Makefile" has been created. + + $ hopen --build + Building in foo/built + + And your software is ready to go! + + See App::hopen::Conventions for information on writing ".hopen.pl" files. + +SYNOPSIS hopen [options] [--] [destination dir [project dir]] If no project directory is specified, the current directory is used. If no destination directory is specified, "/built" is used. - See App::hopen::Conventions for more details. + See App::hopen and App::hopen::Conventions for more details. OPTIONS -a "architecture" @@ -99,6 +123,10 @@ SUPPORT + * This distribution + + See the "eg/" directory distributed with this software for examples. + LICENSE AND COPYRIGHT Copyright (c) 2018--2019 Christopher White. All rights reserved. diff --git a/README.md b/README.md index 50bb775..0bf7b3f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # App::hopen - hopen build system command-line interface -[![Appveyor Badge](https://ci.appveyor.com/api/projects/status/github/hopenbuild/app-hopen?svg=true)](https://ci.appveyor.com/project/cxw42/app-hopen) +[![Appveyor Status](https://img.shields.io/appveyor/ci/cxw42/app-hopen.svg?logo=appveyor)](https://ci.appveyor.com/project/cxw42/app-hopen) [![Travis Status](https://img.shields.io/travis/hopenbuild/app-hopen.svg?logo=travis)](https://travis-ci.org/hopenbuild/app-hopen) @@ -17,12 +17,37 @@ build scripts (specifically, Perl 5.14+) - Context-sensitivity. Your users can tweak their own builds for their own platforms without affecting your project. -See [App::hopen::Conventions](https://github.com/hopenbuild/App-hopen/blob/master/lib/App/hopen/Conventions.pod) for details of the input format. +See [App::hopen::Conventions](https://metacpan.org/pod/App::hopen::Conventions) for details of the input format. Why Perl? Because (1) you probably already have it installed, and (2) it is the original write-once, run-everywhere language! -# USAGE +## Example + +Create a file `.hopen.pl` in your source tree. Then: + + $ hopen + From ``.'' into ``built'' + Running Check phase + +Now `built/MY.hopen.pl` has been created, and loaded with information about +your configuration. You can edit that file if you want to change what will +happen next. + + $ hopen + From ``.'' into ``built'' + Running Gen phase + +Now `built/Makefile` has been created. + + $ hopen --build + Building in foo/built + +And your software is ready to go! + +See [App::hopen::Conventions](https://metacpan.org/pod/App::hopen::Conventions) for information on writing `.hopen.pl` files. + +# SYNOPSIS hopen [options] [--] [destination dir [project dir]] @@ -30,7 +55,7 @@ If no project directory is specified, the current directory is used. If no destination directory is specified, `/built` is used. -See [App::hopen::Conventions](https://github.com/hopenbuild/App-hopen/blob/master/lib/App/hopen/Conventions.pod) for more details. +See [App::hopen](https://metacpan.org/pod/App::hopen) and [App::hopen::Conventions](https://metacpan.org/pod/App::hopen::Conventions) for more details. # OPTIONS @@ -115,6 +140,10 @@ You can also look for information at: [https://metacpan.org/pod/App::hopen](https://metacpan.org/pod/App::hopen) +- This distribution + + See the `eg/` directory distributed with this software for examples. + # LICENSE AND COPYRIGHT Copyright (c) 2018--2019 Christopher White. All rights reserved. diff --git a/bin/hopen b/bin/hopen index c9063a1..47b3b76 100755 --- a/bin/hopen +++ b/bin/hopen @@ -1,7 +1,73 @@ #!perl -# Copyright (c) 2018 cxw42. All rights reserved. See LICENSE file. +# Copyright (c) 2018--2019 cxw42. All rights reserved. See LICENSE file. # To run this manually from the source tree, do # perl -Ilib bin/hopen -use App::hopen; +use strict; use warnings; use App::hopen; exit(App::hopen::Main(\@ARGV)); +__END__ + +=head1 NAME + +hopen - CLI for the App::hopen build system + +=head1 SYNOPSIS + +See L. Basic usage: + + hopen [options] [destination directory] [project directory] + +The default C is C. +The default C is C<.>. + +=head1 AUTHOR + +Christopher White, C + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command. + + perldoc App::hopen For command-line options + perldoc App::hopen::Conventions For terminology and workflow + perldoc Data::Hopen For internals + +You can also look for information at: + +=over 4 + +=item * GitHub: The project's main repository and issue tracker + +L + +=item * MetaCPAN + +L + +=item * This distribution + +See the C directory distributed with this software for examples. + +=back + +=head1 LICENSE AND COPYRIGHT + +Copyright (c) 2018--2019 Christopher White. All rights reserved. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +=cut + # vi: set ts=4 sts=4 sw=4 et ai ft=perl: # diff --git a/t/samples/01/01.hopen.pl b/eg/001-single-file-hello/001.hopen.pl similarity index 91% rename from t/samples/01/01.hopen.pl rename to eg/001-single-file-hello/001.hopen.pl index 24f878b..9b834a0 100644 --- a/t/samples/01/01.hopen.pl +++ b/eg/001-single-file-hello/001.hopen.pl @@ -1,4 +1,4 @@ -# t/samples/01/01.hopen.pl +# 001-single-file-hello/001.hopen.pl use language 'C'; # uses ::C, and makes `C` an alias for it. # The "language" package is synthesized by Data::Hopen::HopenFileKit. diff --git a/eg/001-single-file-hello/README.md b/eg/001-single-file-hello/README.md new file mode 100644 index 0000000..0a1ecc6 --- /dev/null +++ b/eg/001-single-file-hello/README.md @@ -0,0 +1,3 @@ +# 001-single-file-hello + +As basic as it gets: compile one C file and link it into an executable. diff --git a/t/samples/01/hello.c b/eg/001-single-file-hello/hello.c similarity index 100% rename from t/samples/01/hello.c rename to eg/001-single-file-hello/hello.c diff --git a/t/samples/01/runhopen b/eg/001-single-file-hello/runhopen similarity index 71% rename from t/samples/01/runhopen rename to eg/001-single-file-hello/runhopen index 9faeb7b..91cbef2 100755 --- a/t/samples/01/runhopen +++ b/eg/001-single-file-hello/runhopen @@ -7,4 +7,4 @@ else dbg="" fi -perl -I../../../lib $dbg ../../../bin/hopen "$@" +perl -I../../lib $dbg ../../bin/hopen "$@" diff --git a/eg/002-double-file-hello/.hopen.pl b/eg/002-double-file-hello/.hopen.pl new file mode 100644 index 0000000..e28c8da --- /dev/null +++ b/eg/002-double-file-hello/.hopen.pl @@ -0,0 +1,15 @@ +# 002-double-file-hello/.hopen.pl + +use language 'C'; # uses ::C, and makes `C` an alias for it. + # The "language" package is synthesized by Data::Hopen::HopenFileKit. + +on check => {}; # Nothing to do during the Check phase + +$Build + ->H::files(qw(hello.c printmsg.c), -name=>'FilesHello') + # Two source files. + ->C::compile(-name=>'CompileHello') + # CompileHello will compile both of the C files listed above... + ->C::link('hello', -name=>'LinkHello') + # ... and LinkHello will link them together into a single executable. + ->default_goal; diff --git a/eg/002-double-file-hello/README.md b/eg/002-double-file-hello/README.md new file mode 100644 index 0000000..00f7974 --- /dev/null +++ b/eg/002-double-file-hello/README.md @@ -0,0 +1,4 @@ +# 002-double-file-hello + +An example of compiling two files and linking them together. In this example, +unlike 003, both files are compiled by a single `C::compile` node. diff --git a/eg/002-double-file-hello/hello.c b/eg/002-double-file-hello/hello.c new file mode 100644 index 0000000..4585327 --- /dev/null +++ b/eg/002-double-file-hello/hello.c @@ -0,0 +1,7 @@ +extern void print_the_message(); + +int main(void) +{ + print_the_message(); + return 0; +} diff --git a/eg/002-double-file-hello/printmsg.c b/eg/002-double-file-hello/printmsg.c new file mode 100644 index 0000000..97a0550 --- /dev/null +++ b/eg/002-double-file-hello/printmsg.c @@ -0,0 +1,6 @@ +#include + +void print_the_message() +{ + printf("Hello, world!\n"); +} diff --git a/eg/002-double-file-hello/runhopen b/eg/002-double-file-hello/runhopen new file mode 100755 index 0000000..91cbef2 --- /dev/null +++ b/eg/002-double-file-hello/runhopen @@ -0,0 +1,10 @@ +#!/bin/sh +# This is a convenience invoker for testing. +if [ "$1" = "-d" ]; then + dbg="-d" + shift +else + dbg="" +fi + +perl -I../../lib $dbg ../../bin/hopen "$@" diff --git a/eg/003-double-file-double-compile/.hopen.pl b/eg/003-double-file-double-compile/.hopen.pl new file mode 100644 index 0000000..5dfe68f --- /dev/null +++ b/eg/003-double-file-double-compile/.hopen.pl @@ -0,0 +1,26 @@ +# 003-double-file-double-compile/.hopen.pl + +use language 'C'; # uses ::C, and makes `C` an alias for it. + # The "language" package is synthesized by Data::Hopen::HopenFileKit. + +on check => {}; # Nothing to do during the Check phase + +# Executable +my $exe = $Build + ->C::link('hello', -name=>'LinkHello'); + # link the inputs together into a single executable. + +# First source file +my $hello_o = $Build + ->H::files('hello.c', -name=>'hello.c') + ->C::compile(-name=>'Compile hello.c'); + +# Second source file +my $printmsg_o = $Build + ->H::files('printmsg.c', -name=>'printmsg.c') + ->C::compile(-name=>'Compile printmsg.c'); + +# Finally, connect everything together. The default_goal call should be last. +$hello_o->to($exe); +$printmsg_o->to($exe); +$exe->default_goal; diff --git a/eg/003-double-file-double-compile/README.md b/eg/003-double-file-double-compile/README.md new file mode 100644 index 0000000..edba47e --- /dev/null +++ b/eg/003-double-file-double-compile/README.md @@ -0,0 +1,4 @@ +# 003-double-file-compile + +An example of compiling two files and linking them together. In this example, +unlike 002, each file is compiled by a separate `C::compile` node. diff --git a/eg/003-double-file-double-compile/hello.c b/eg/003-double-file-double-compile/hello.c new file mode 100644 index 0000000..4585327 --- /dev/null +++ b/eg/003-double-file-double-compile/hello.c @@ -0,0 +1,7 @@ +extern void print_the_message(); + +int main(void) +{ + print_the_message(); + return 0; +} diff --git a/eg/003-double-file-double-compile/printmsg.c b/eg/003-double-file-double-compile/printmsg.c new file mode 100644 index 0000000..97a0550 --- /dev/null +++ b/eg/003-double-file-double-compile/printmsg.c @@ -0,0 +1,6 @@ +#include + +void print_the_message() +{ + printf("Hello, world!\n"); +} diff --git a/eg/003-double-file-double-compile/runhopen b/eg/003-double-file-double-compile/runhopen new file mode 100755 index 0000000..91cbef2 --- /dev/null +++ b/eg/003-double-file-double-compile/runhopen @@ -0,0 +1,10 @@ +#!/bin/sh +# This is a convenience invoker for testing. +if [ "$1" = "-d" ]; then + dbg="-d" + shift +else + dbg="" +fi + +perl -I../../lib $dbg ../../bin/hopen "$@" diff --git a/lib/App/hopen.pm b/lib/App/hopen.pm index 07c3dd8..f149a41 100755 --- a/lib/App/hopen.pm +++ b/lib/App/hopen.pm @@ -1,8 +1,9 @@ # App::hopen: Implementation of the hopen(1) program package App::hopen; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; # Imports {{{1 +use strict; use Data::Hopen::Base; use App::hopen::AppUtil ':all'; @@ -43,7 +44,7 @@ use constant EXIT_PARAM_ERR => 2; # couldn't understand the command line App::hopen - hopen build system command-line interface -=head1 SYNOPSIS +=head1 INTRODUCTION (Note: most features are not yet implemented ;) . However it will generate a Makefile for a basic C program at this point!) @@ -75,7 +76,32 @@ See L for details of the input format. Why Perl? Because (1) you probably already have it installed, and (2) it is the original write-once, run-everywhere language! -=head1 USAGE +=head2 Example + +Create a file C<.hopen.pl> in your source tree. Then: + + $ hopen + From ``.'' into ``built'' + Running Check phase + +Now C has been created, and loaded with information about +your configuration. You can edit that file if you want to change what will +happen next. + + $ hopen + From ``.'' into ``built'' + Running Gen phase + +Now C has been created. + + $ hopen --build + Building in foo/built + +And your software is ready to go! + +See L for information on writing C<.hopen.pl> files. + +=head1 SYNOPSIS hopen [options] [--] [destination dir [project dir]] @@ -83,7 +109,72 @@ If no project directory is specified, the current directory is used. If no destination directory is specified, C<< /built >> is used. -See L for more details. +See L and L for more details. + +=head1 OPTIONS + +=over + +=item -a C + +Specify the architecture. This is an arbitrary string interpreted by the +generator or toolset. + +=item -e C + +Add the C as if it were a hopen file. C<-e> files are processed +after all other hopen files, so can modify anything that has been set up +by those files. Can be specified more than once. + +=item --fresh + +Start a fresh build --- ignore any C file that may exist in +the destination directory. + +=item --from C + +Specify the project directory. Overrides a project directory given as a +positional argument. + +=item -g C + +Specify the generator. The given C should be either a full package +name or the part after C. + +=item -t C + +Specify the toolset. The given C should be either a full package +name or the part after C. + +=item --to C + +Specify the destination directory. Overrides a destination directory given +as a positional argument. + +=item --phase C + +Specify which phase of the process to run. Note that this overrides whatever +is specified in any MY.hopen.pl file, so may cause unexpected results! + +If C<--phase> is given, no other hopen file can set the phase, and hopen will +terminate if a file attempts to do so. + +=item -q + +Produce no output (quiet). Overrides C<-v>. + +=item -v, --verbose=n + +Verbose. Specify more C's for more verbosity. At present, C<-vv> +(equivalently, C<--verbose=2>) gives +you detailed traces of the data, and C<-vvv> gives you more detailed +code tracebacks on error. + +=item --version + +Print the version of hopen and exit + +=back =head1 INTERNALS @@ -220,7 +311,7 @@ values from the command line, keyed by the keys in L. Pod::Usage::pod2usage( -exitval => EXIT_OK, @in, - -verbose => 99, -sections => '!INTERNALS' # suppress + -verbose => 99, -sections => '!INTERNALS' # suppress INTERNALS ) if have('man'); } @@ -581,6 +672,8 @@ sub _inner { # Run a single invocation of hopen(1). {{{2 Do the work for one invocation of hopen(1). Dies on failure. Main() then translates the die() into a print and error return. +Takes a hash of options. + The return value of _inner is unspecified and ignored. =cut @@ -588,6 +681,13 @@ The return value of _inner is unspecified and ignored. my %opts = @_; local $_hrData = {}; + # TODO FIXME. This is a bit of a hack: Reset global variables on --fresh. + # Instead, App::hopen should be a class, and each instance should have its + # own data (I think). + if($opts{FRESH}) { + $_did_set_phase = false; + } + if($opts{PRINT_VERSION}) { # print version, raw and dotted if($App::hopen::VERSION =~ m<^([^\.]+)\.(\d{3})(\d{3})>) { printf "hopen version %d.%d.%d ($App::hopen::VERSION)\n", $1, $2, $3; @@ -627,7 +727,7 @@ EOT # Prohibit builds if there's a MY.hopen.pl file in the project directory, # since those are the marker of a destination directory. - die <file(MYH); + if(-e $proj_dir->file(MYH)) { die <>. 1; __END__ -# === Command-line usage documentation ================================== {{{1 - -=head1 OPTIONS - -=over - -=item -a C - -Specify the architecture. This is an arbitrary string interpreted by the -generator or toolset. - -=item -e C - -Add the C as if it were a hopen file. C<-e> files are processed -after all other hopen files, so can modify anything that has been set up -by those files. Can be specified more than once. - -=item --fresh - -Start a fresh build --- ignore any C file that may exist in -the destination directory. - -=item --from C - -Specify the project directory. Overrides a project directory given as a -positional argument. - -=item -g C - -Specify the generator. The given C should be either a full package -name or the part after C. - -=item -t C - -Specify the toolset. The given C should be either a full package -name or the part after C. - -=item --to C - -Specify the destination directory. Overrides a destination directory given -as a positional argument. - -=item --phase C - -Specify which phase of the process to run. Note that this overrides whatever -is specified in any MY.hopen.pl file, so may cause unexpected results! - -If C<--phase> is given, no other hopen file can set the phase, and hopen will -terminate if a file attempts to do so. - -=item -q - -Produce no output (quiet). Overrides C<-v>. - -=item -v, --verbose=n - -Verbose. Specify more C's for more verbosity. At present, C<-vv> -(equivalently, C<--verbose=2>) gives -you detailed traces of the data, and C<-vvv> gives you more detailed -code tracebacks on error. - -=item --version - -Print the version of hopen and exit - -=back +# === Rest of the documentation ========================================= {{{1 =head1 AUTHOR @@ -935,7 +970,7 @@ You can find documentation for this module with the perldoc command. You can also look for information at: -=over 4 +=over =item * GitHub: The project's main repository and issue tracker @@ -945,6 +980,10 @@ L L +=item * This distribution + +See the C directory distributed with this software for examples. + =back =head1 LICENSE AND COPYRIGHT diff --git a/lib/App/hopen/AppUtil.pm b/lib/App/hopen/AppUtil.pm index e24f95d..8943b14 100755 --- a/lib/App/hopen/AppUtil.pm +++ b/lib/App/hopen/AppUtil.pm @@ -1,10 +1,11 @@ # App::hopen::AppUtil - utility routines used by App::hopen::App package App::hopen::AppUtil; use Data::Hopen qw(:default isMYH MYH); +use strict; use Data::Hopen::Base; use parent 'Exporter'; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS); BEGIN { diff --git a/lib/App/hopen/Asset.pm b/lib/App/hopen/Asset.pm index ef96228..3e33b19 100755 --- a/lib/App/hopen/Asset.pm +++ b/lib/App/hopen/Asset.pm @@ -1,11 +1,12 @@ # App::hopen::Asset - record representing a file to be produced package App::hopen::Asset; +use strict; use Data::Hopen::Base; use Path::Class; # and we use Class::Tiny below. -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; # Docs {{{1 diff --git a/lib/App/hopen/BuildSystemGlobals.pm b/lib/App/hopen/BuildSystemGlobals.pm index 1f0c9ea..4b37549 100755 --- a/lib/App/hopen/BuildSystemGlobals.pm +++ b/lib/App/hopen/BuildSystemGlobals.pm @@ -1,9 +1,10 @@ # App::hopen::BuildSystemGlobals - global data for build-system use cases. package App::hopen::BuildSystemGlobals; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'Exporter'; our @EXPORT; diff --git a/lib/App/hopen/Conventions.pod b/lib/App/hopen/Conventions.pod index 158782d..7860788 100755 --- a/lib/App/hopen/Conventions.pod +++ b/lib/App/hopen/Conventions.pod @@ -35,7 +35,7 @@ The generator's job is to arrange for coordinating work that needs to be done. =item Toolset -A collection of operations (L subclasses) that know +A collection of commands (L subclasses) that know how to process specific types of files. Toolsets are responsible for defining the work that the generator will coordinate. diff --git a/lib/App/hopen/G/AssetOp.pm b/lib/App/hopen/G/AssetOp.pm index 06de50e..36be748 100755 --- a/lib/App/hopen/G/AssetOp.pm +++ b/lib/App/hopen/G/AssetOp.pm @@ -1,10 +1,11 @@ # App::hopen::G::AssetOp - parent class for operations used by a # generator to build an asset package App::hopen::G::AssetOp; +use strict; use Data::Hopen::Base; use Quote::Code; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'App::hopen::G::Cmd'; # we use Class::Tiny below diff --git a/lib/App/hopen/G/Cmd.pm b/lib/App/hopen/G/Cmd.pm index 6a82255..cb8496a 100644 --- a/lib/App/hopen/G/Cmd.pm +++ b/lib/App/hopen/G/Cmd.pm @@ -1,9 +1,10 @@ # App::hopen::G::Cmd - base class for hopen(1) command-graph nodes package App::hopen::G::Cmd; +use strict; use Data::Hopen::Base; use Quote::Code; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'Data::Hopen::G::Op'; use Class::Tiny { @@ -17,7 +18,7 @@ use Data::Hopen qw(getparameters); =head1 NAME -App::hopen::G::Cmd - base class for hopen(1) operation-graph nodes +App::hopen::G::Cmd - base class for hopen(1) command-graph nodes =head1 SYNOPSIS @@ -29,7 +30,7 @@ L. See L. =head2 made An arrayref of the outputs from this function, which are L -instances. (TODO enforce this requirement.) +instances. =cut @@ -94,8 +95,8 @@ sub input_assets { my $hrSourceFiles = $self->scope->find(-name => 'made', -set => '*', -levels => 'local') // {}; - if(scalar keys %$hrSourceFiles) { - $lrSourceFiles = %$hrSourceFiles{(keys %$hrSourceFiles)[0]}; + if(keys %$hrSourceFiles) { + $lrSourceFiles = $hrSourceFiles->{(keys %$hrSourceFiles)[0]}; } else { $lrSourceFiles = []; } diff --git a/lib/App/hopen/G/FilesCmd.pm b/lib/App/hopen/G/FilesCmd.pm index c42f7fc..6309611 100644 --- a/lib/App/hopen/G/FilesCmd.pm +++ b/lib/App/hopen/G/FilesCmd.pm @@ -1,9 +1,10 @@ # App::hopen::G::FilesCmd - Cmd that outputs a list of files. package App::hopen::G::FilesCmd; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'App::hopen::G::Cmd'; use Class::Tiny { diff --git a/lib/App/hopen/Gen.pm b/lib/App/hopen/Gen.pm index 1394ea4..e5f1d6d 100644 --- a/lib/App/hopen/Gen.pm +++ b/lib/App/hopen/Gen.pm @@ -1,9 +1,10 @@ # App::hopen::Gen - base class for hopen generators package App::hopen::Gen; use Data::Hopen qw(:default $QUIET); +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'Data::Hopen::Visitor'; use Class::Tiny qw(proj_dir dest_dir), { diff --git a/lib/App/hopen/Gen/Make.pm b/lib/App/hopen/Gen/Make.pm index 7664674..74b0949 100755 --- a/lib/App/hopen/Gen/Make.pm +++ b/lib/App/hopen/Gen/Make.pm @@ -1,8 +1,9 @@ # App::hopen::Gen::Make - generator for a generic make(1). package App::hopen::Gen::Make; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'App::hopen::Gen'; use Class::Tiny qw(targets); @@ -66,7 +67,7 @@ sub visit_goal { die 'No input files to goal ' . $args{goal}->name unless scalar keys %$hrSourceFiles; - my $lrSourceFiles = %$hrSourceFiles{(keys %$hrSourceFiles)[0]}; + my $lrSourceFiles = $hrSourceFiles->{(keys %$hrSourceFiles)[0]}; hlog { 'found inputs to goal', $args{goal}->name, Dumper($lrSourceFiles) } 2; # TODO? verify that all the assets are actually in the graph first? @@ -74,18 +75,6 @@ sub visit_goal { } #visit_goal() -#=head2 visit_node -# -# TODO Build the list of work to do each time we visit a node, rather -# than requiring the generator to build the work[] array. -# -#=cut -# -#sub visit_node { -# my $self = shift or croak 'Need an instance'; -# ... -#} #visit_node() - =head2 finalize Write out the Makefile. @@ -157,7 +146,6 @@ sub _run_build { foreach my $candidate (qw[make gmake mingw32-make dmake]) { my $path = File::Which::which($candidate); next unless defined $path; - # TODO cd into dest dir hlog { Running => $path }; system $path, (); return; diff --git a/lib/App/hopen/Gen/Make/AssetGraphNode.pm b/lib/App/hopen/Gen/Make/AssetGraphNode.pm index 4e6b39d..aceea8a 100644 --- a/lib/App/hopen/Gen/Make/AssetGraphNode.pm +++ b/lib/App/hopen/Gen/Make/AssetGraphNode.pm @@ -1,9 +1,10 @@ # App::hopen::Gen::Make::AssetGraphNode - AssetOp for Gen::Make package App::hopen::Gen::Make::AssetGraphNode; use Data::Hopen qw(getparameters $VERBOSE); +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'App::hopen::G::AssetOp'; use Class::Tiny; @@ -53,7 +54,7 @@ sub _run { print $fh qc' # Depends on {$_->target}\n' foreach @inputs; } - if($self->how) { + if(defined $self->how) { my @paths = map { $_->target->path_wrt($DestDir) } @inputs; my $recipe = $self->how; # TODO refactor this processing into a utility module/function diff --git a/lib/App/hopen/Gen/Make/AssetGraphVisitor.pm b/lib/App/hopen/Gen/Make/AssetGraphVisitor.pm index d00de9a..7a35c44 100755 --- a/lib/App/hopen/Gen/Make/AssetGraphVisitor.pm +++ b/lib/App/hopen/Gen/Make/AssetGraphVisitor.pm @@ -1,9 +1,10 @@ # App::hopen::Gen::Make::AssetGraphVisitor - visitor to write goals package App::hopen::Gen::Make::AssetGraphVisitor; use Data::Hopen qw(hlog getparameters $VERBOSE); +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'Data::Hopen::Visitor'; use Class::Tiny; @@ -16,7 +17,7 @@ use Quote::Code; =head1 NAME -# App::hopen::Gen::Make::AssetGraphVisitor - visitor to write goals +App::hopen::Gen::Make::AssetGraphVisitor - visitor to write goals =head1 SYNOPSIS @@ -48,7 +49,7 @@ sub visit_goal { die 'No input files to goal ' . $args{goal}->name unless scalar keys %$hrInputs; - my $lrInputs = %$hrInputs{(keys %$hrInputs)[0]}; + my $lrInputs = $hrInputs->{(keys %$hrInputs)[0]}; hlog { __PACKAGE__, 'found inputs to goal', $args{goal}->name, Dumper($lrInputs) } 2; my @paths = map { $_->target->path_wrt($DestDir) } @$lrInputs; diff --git a/lib/App/hopen/Gen/Ninja.pm b/lib/App/hopen/Gen/Ninja.pm new file mode 100755 index 0000000..2423daa --- /dev/null +++ b/lib/App/hopen/Gen/Ninja.pm @@ -0,0 +1,193 @@ +# App::hopen::Gen::Ninja - generator for ninja(1). +package App::hopen::Gen::Ninja; +use strict; +use Data::Hopen::Base; + +# TODO reduce code duplication between this and Gen::Make +# TODO here and in Gen::Make, remove the need for AssetGraphVisitor by +# creating an extra AssetGraphNode to represent the goal. + +our $VERSION = '0.000011'; + +use parent 'App::hopen::Gen'; +use Class::Tiny; + +use App::hopen::BuildSystemGlobals; +use App::hopen::Phases qw(is_last_phase); +use Data::Hopen qw(:default getparameters $QUIET); +use Data::Hopen::Scope::Hash; +use Data::Hopen::Util::Data qw(forward_opts); +use File::Which; +use Hash::Ordered; +use Path::Class; +use Quote::Code; + +use App::hopen::Gen::Ninja::AssetGraphNode; # for $OUTPUT +use App::hopen::Gen::Ninja::AssetGraphVisitor; + +# Docs {{{1 + +=head1 NAME + +Data::Hopen::Gen::Ninja - hopen generator for simple Ninja files + +=head1 SYNOPSIS + +This generator makes a build.ninja file. + +=head1 FUNCTIONS + +=cut + +# }}}1 + +=head2 visit_goal + +Add a target corresponding to the name of the goal. Usage: + + $Generator->visit_goal($node, $node_inputs); + +This happens while the command graph is being run. + +=cut + +sub visit_goal { + my ($self, %args) = getparameters('self', [qw(goal node_inputs)], @_); + + # --- Add the goal to the asset graph --- + + my $asset_goal = $self->_assets->goal($args{goal}->name); + + # Pull the inputs. TODO refactor out the code in common with + # AhG::Cmd::input_assets(). + my $hrSourceFiles = + $args{node_inputs}->find(-name => 'made', + -set => '*', -levels => 'local') // {}; + die 'No input files to goal ' . $args{goal}->name + unless scalar keys %$hrSourceFiles; + + my $lrSourceFiles = $hrSourceFiles->{(keys %$hrSourceFiles)[0]}; + hlog { 'found inputs to goal', $args{goal}->name, Dumper($lrSourceFiles) } 2; + + # TODO? verify that all the assets are actually in the graph first? + $self->connect($_, $asset_goal) foreach @$lrSourceFiles; + +} #visit_goal() + +=head2 finalize + +Write out the Ninja file. + +=cut + +sub finalize { + my ($self, %args) = getparameters('self', [qw(phase dag data)], @_); + hlog { Finalizing => __PACKAGE__ , '- phase', $args{phase} }; + return unless is_last_phase $args{phase}; + + hlog { __PACKAGE__, 'Asset graph', '' . $self->_assets->_graph } 3; + + # During the Gen phase, create the Ninja file + open my $fh, '>', $self->dest_dir->file('build.ninja') + or die "Couldn't create Ninjafile"; + print $fh qc_to <<"EOT"; +# Ninja file generated by hopen (https://github.com/hopenbuild/App-hopen) +# at #{gmtime} GMT +# From ``#{$self->proj_dir->absolute}'' into ``#{$self->dest_dir->absolute}'' + +EOT + + my $context = Data::Hopen::Scope::Hash->new; + $context->put(App::hopen::Gen::Ninja::AssetGraphNode::OUTPUT, $fh); + + # Write the Ninja file. TODO? flip the order? + + $self->_assets->run(-context => $context, + -visitor => App::hopen::Gen::Ninja::AssetGraphVisitor->new, + forward_opts(\%args, {'-'=>1}, qw(phase)) + ); + + close $fh; +} #finalize() + +=head2 default_toolset + +Returns the package name of the default toolset for this generator, +which is C (i.e., L). + +=cut + +sub default_toolset { 'Gnu' } + +=head2 _assetop_class + +The class of asset-graph operations, which in this case is +L. + +=cut + +sub _assetop_class { 'App::hopen::Gen::Ninja::AssetGraphNode' } + +=head2 _run_build + +Implementation of L. + +=cut + +sub _run_build { + # Look for the make(1) executable. Listing make before gmake since a + # system with both Cygwin and Strawberry Perl installed has cygwin's + # make(1) and Strawberry's gmake(1). + my $path = File::Which::which('ninja'); + if(defined $path) { + hlog { Running => $path }; + system $path, (); + } else { + warn "Could not find the 'ninja' program"; + } +} #_run_build() + +=head1 INTERNALS + +=head2 _expand + +Produce the command line or lines associated with a work item. Used by +L. + +=cut + +sub _expand { + my $item = shift or croak 'Need a work item'; + hlog { __PACKAGE__ . '::_expand()', Dumper($item) } 2; + my $retval = $item->{how} or return ''; # no `how` => no output; not an error + $retval = $retval->[0] if ref $retval eq 'ARRAY'; + + my $first = $item->{from}->[0]; + $first = $first->orig->relative($DestDir) + if $first->DOES('App::hopen::Util::BasedPath'); + + my $out = $item->{to}->[0]; + $out = $out->orig->relative($DestDir) + if $out->DOES('App::hopen::Util::BasedPath'); + + $retval =~ s{#first\b}{$first // ''}ge; # first input + $retval =~ s{#all\b}{join(' ', @{$item->{from}})}ge; # all inputs + $retval =~ s{#out\b}{$out // ''}ge; + + return $retval; +} #_expand() + +=head2 BUILD + +Constructor + +=cut + +sub BUILD { + my ($self, $hrArgs) = @_; +} #BUILD() + + +1; +__END__ +# vi: set fdm=marker: # diff --git a/lib/App/hopen/Gen/Ninja/AssetGraphNode.pm b/lib/App/hopen/Gen/Ninja/AssetGraphNode.pm new file mode 100644 index 0000000..81bcf16 --- /dev/null +++ b/lib/App/hopen/Gen/Ninja/AssetGraphNode.pm @@ -0,0 +1,95 @@ +# App::hopen::Gen::Ninja::AssetGraphNode - AssetOp for Gen::Ninja +package App::hopen::Gen::Ninja::AssetGraphNode; +use Data::Hopen qw(getparameters $VERBOSE); +use strict; +use Data::Hopen::Base; + +our $VERSION = '0.000011'; + +use parent 'App::hopen::G::AssetOp'; +use Class::Tiny { + _rules => sub { +{} }, +}; + +use App::hopen::BuildSystemGlobals; # for $DestDir +use Quote::Code; +use String::Print; + +# Docs {{{1 + +=head1 NAME + +App::hopen::Gen::Ninja::AssetGraphNode - AssetOp for Gen::Ninja + +=head1 SYNOPSIS + +TODO + +=head1 ATTRIBUTES + +=head2 _rules + +TODO? Store mapping from command lines to rules? Don't want to generate +a separate rule for every command if we can help it. + +=head1 FUNCTIONS + +=cut + +# }}}1 + +use vars::i '&OUTPUT' => sub { '__R_Ninjafile' }; + +=head2 _run + +Generate a piece of a Ninja file and write it to the filehandle in +C<__R_Ninjafile>. + +=cut + +sub _run { + state $ruleidx=0; + + my ($self, %args) = getparameters('self', [qw(; phase visitor)], @_); + my $fh = $self->scope->find(OUTPUT); + # TODO deal with multiple inputs being merged in DAG::_run() + + my @inputs = $self->input_assets; + my $output = $self->asset->target->path_wrt($DestDir); + # TODO refactor this processing into a utility module/function + + # Debugging output + if($VERBOSE) { + print $fh qc'\n# From node {$self->name}:\n'; + print $fh qc' # {$self->how//""}\n'; + print $fh qc' # Depends on {$_->target}\n' foreach @inputs; + } + + if(defined $self->how) { + my @paths = map { $_->target->path_wrt($DestDir) } @inputs; + my $recipe = $self->how; + # TODO refactor this processing into a utility module/function + warn "I don't yet support #first very well (in ``$recipe'')" if $recipe =~ /#first/; + $recipe =~ s<#first\b><\$in>g; # first input # TODO FIXME + $recipe =~ s<#all\b><\$in>g; # all inputs + $recipe =~ s<#out\b><\$out>g; + + # TODO FIXME ugly hack: for now, each command gets its own rule. + my $rulename = 'rule_' . ++$ruleidx; + print $fh qc_to <<"EOT" +rule #{$rulename} + command = #{$recipe} + +build #{$output}: #{$rulename} #{join(" ", @paths)} + +EOT + + } + + $self->make($self->asset); + return {}; +} #_run() + +1; +__END__ +# vi: set fdm=marker: # diff --git a/lib/App/hopen/Gen/Ninja/AssetGraphVisitor.pm b/lib/App/hopen/Gen/Ninja/AssetGraphVisitor.pm new file mode 100755 index 0000000..4bef61c --- /dev/null +++ b/lib/App/hopen/Gen/Ninja/AssetGraphVisitor.pm @@ -0,0 +1,71 @@ +# App::hopen::Gen::Ninja::AssetGraphVisitor - visitor to write goals +package App::hopen::Gen::Ninja::AssetGraphVisitor; +use Data::Hopen qw(hlog getparameters $VERBOSE); +use strict; +use Data::Hopen::Base; + +our $VERSION = '0.000011'; + +use parent 'Data::Hopen::Visitor'; +use Class::Tiny; + +use App::hopen::BuildSystemGlobals; # for $DestDir +use App::hopen::Gen::Ninja::AssetGraphNode; # for OUTPUT +use Quote::Code; + +# Docs {{{1 + +=head1 NAME + +App::hopen::Gen::Ninja::AssetGraphVisitor - visitor to write goals + +=head1 SYNOPSIS + +This is the visitor used when L traverses the +asset graph. Its purpose is to tie the inputs to each goal into that goal. + +=head1 FUNCTIONS + +=cut + +# }}}1 + +=head2 visit_goal + +Write a goal entry to the Ninja file being built. +This happens while the asset graph is being run. + +=cut + +sub visit_goal { + my ($self, %args) = getparameters('self', [qw(goal node_inputs)], @_); + my $fh = $args{node_inputs}->find(App::hopen::Gen::Ninja::AssetGraphNode::OUTPUT); + + # Pull the inputs. TODO refactor out the code in common with + # AhG::Cmd::input_assets(). + my $hrInputs = + $args{node_inputs}->find(-name => 'made', + -set => '*', -levels => 'local') // {}; + die 'No input files to goal ' . $args{goal}->name + unless scalar keys %$hrInputs; + + my $lrInputs = $hrInputs->{(keys %$hrInputs)[0]}; + hlog { __PACKAGE__, 'found inputs to goal', $args{goal}->name, Dumper($lrInputs) } 2; + + my @paths = map { $_->target->path_wrt($DestDir) } @$lrInputs; + say $fh qc'\n# === Ninja file goal {$args{goal}->name}' if $VERBOSE; + say $fh qc'build {$args{goal}->name}: phony {join " ", @paths}'; + say $fh qc'default {$args{goal}->name}'; +} #visit_goal() + +=head2 visit_node + +No-op. + +=cut + +sub visit_node { } + +1; +__END__ +# vi: set fdm=marker: # diff --git a/lib/App/hopen/H.pm b/lib/App/hopen/H.pm index 4786f0b..02933d6 100755 --- a/lib/App/hopen/H.pm +++ b/lib/App/hopen/H.pm @@ -1,8 +1,9 @@ # App::hopen::H - H:: namespace for use in hopen files package App::hopen::H; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'Exporter'; use vars::i '@EXPORT' => []; diff --git a/lib/App/hopen/HopenFileKit.pm b/lib/App/hopen/HopenFileKit.pm index 06d3f8c..0bcba92 100755 --- a/lib/App/hopen/HopenFileKit.pm +++ b/lib/App/hopen/HopenFileKit.pm @@ -1,5 +1,6 @@ # App::hopen::HopenFileKit - set up a hopen file package App::hopen::HopenFileKit; +use strict; use Data::Hopen::Base; use Import::Into; @@ -15,7 +16,7 @@ use App::hopen::Phases (); use Data::Hopen qw(:default loadfrom); -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'Exporter'; # Exporter-exported symbols {{{1 our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS); diff --git a/lib/App/hopen/Phase.pod b/lib/App/hopen/Phase.pod index 1b4db48..38a20bc 100644 --- a/lib/App/hopen/Phase.pod +++ b/lib/App/hopen/Phase.pod @@ -1,20 +1,20 @@ =head1 NAME -Data::Hopen::Phase - Namespace for hopen phases. +App::hopen::Phase - Namespace for hopen phases. =head1 SYNOPSIS -The specific phases in hopen live under C. The +The specific phases in hopen live under C. The current phases are: =over -=item L +=item L Read a foundations file and one or more context files; output a capability file and an options file. The user may edit the options file. -=item L +=item L Read the capability, option, and context files, plus a recipes file identifying the build graph. Output one or more blueprint files. diff --git a/lib/App/hopen/Phase/Check.pm b/lib/App/hopen/Phase/Check.pm index dd901f9..9e40add 100644 --- a/lib/App/hopen/Phase/Check.pm +++ b/lib/App/hopen/Phase/Check.pm @@ -1,10 +1,11 @@ # App::hopen::Phase::Check - checking-phase operations package App::hopen::Phase::Check; use Data::Hopen; +use strict; use Data::Hopen::Base; use parent 'Exporter'; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS); BEGIN { diff --git a/lib/App/hopen/Phase/Gen.pm b/lib/App/hopen/Phase/Gen.pm index 5a838aa..8079534 100755 --- a/lib/App/hopen/Phase/Gen.pm +++ b/lib/App/hopen/Phase/Gen.pm @@ -1,10 +1,11 @@ # App::hopen::Phase::Gen - generation-phase operations package App::hopen::Phase::Gen; use Data::Hopen; +use strict; use Data::Hopen::Base; #use parent 'Exporter'; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; #use Class::Tiny ;#qw(TODO); diff --git a/lib/App/hopen/Phases.pm b/lib/App/hopen/Phases.pm index 1086825..543d96d 100755 --- a/lib/App/hopen/Phases.pm +++ b/lib/App/hopen/Phases.pm @@ -1,9 +1,10 @@ # App::hopen::Phases - definitions of phases package App::hopen::Phases; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'Exporter'; our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS); diff --git a/lib/App/hopen/T/Gnu.pm b/lib/App/hopen/T/Gnu.pm index 7002e0c..dd5ca2a 100755 --- a/lib/App/hopen/T/Gnu.pm +++ b/lib/App/hopen/T/Gnu.pm @@ -1,9 +1,10 @@ # App::hopen::T::Gnu - GNU toolset package App::hopen::T::Gnu; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; #use parent 'Data::Hopen::TODO'; #use Class::Tiny qw(TODO); diff --git a/lib/App/hopen/T/Gnu/C.pm b/lib/App/hopen/T/Gnu/C.pm index 56046c7..c990331 100755 --- a/lib/App/hopen/T/Gnu/C.pm +++ b/lib/App/hopen/T/Gnu/C.pm @@ -2,9 +2,10 @@ # TODO RESUME HERE - put .o files in the dest dir package App::hopen::T::Gnu::C; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'Exporter'; diff --git a/lib/App/hopen/T/Gnu/C/CompileCmd.pm b/lib/App/hopen/T/Gnu/C/CompileCmd.pm index aa5355b..c60e533 100755 --- a/lib/App/hopen/T/Gnu/C/CompileCmd.pm +++ b/lib/App/hopen/T/Gnu/C/CompileCmd.pm @@ -2,9 +2,10 @@ # TODO RESUME HERE - put .o files in the dest dir package App::hopen::T::Gnu::C::CompileCmd; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'App::hopen::G::Cmd'; use Class::Tiny qw(compiler); @@ -12,13 +13,13 @@ use Class::Tiny qw(compiler); use App::hopen::BuildSystemGlobals; # For $DestDir. # TODO make the dirs available to nodes through the context. use App::hopen::Util::BasedPath; -use Config; +#use Config; use Data::Hopen qw(getparameters); -use Data::Hopen::G::GraphBuilder; +#use Data::Hopen::G::GraphBuilder; #use Data::Hopen::Util::Data qw(forward_opts); use Data::Hopen::Util::Filename; -use Deep::Hash::Utils qw(deepvalue); -use File::Which (); +#use Deep::Hash::Utils qw(deepvalue); +#use File::Which (); use Path::Class; my $_FN = Data::Hopen::Util::Filename->new; # for brevity diff --git a/lib/App/hopen/T/Gnu/C/LinkCmd.pm b/lib/App/hopen/T/Gnu/C/LinkCmd.pm index 998d511..64e45e1 100755 --- a/lib/App/hopen/T/Gnu/C/LinkCmd.pm +++ b/lib/App/hopen/T/Gnu/C/LinkCmd.pm @@ -1,9 +1,10 @@ # App::hopen::T::Gnu::C::LinkCmd - link object files using the GNU toolset package App::hopen::T::Gnu::C::LinkCmd; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'App::hopen::G::Cmd'; use Class::Tiny qw(dest linker); @@ -11,23 +12,22 @@ use Class::Tiny qw(dest linker); use App::hopen::BuildSystemGlobals; # For $DestDir. # TODO make the dirs available to nodes through the context. use App::hopen::Util::BasedPath; -use Config; +#use Config; use Data::Hopen qw(getparameters); -use Data::Hopen::G::GraphBuilder; -#use Data::Hopen::Util::Data qw(forward_opts); +#use Data::Hopen::G::GraphBuilder; use Data::Hopen::Util::Filename; -use Deep::Hash::Utils qw(deepvalue); -use File::Which (); +#use Deep::Hash::Utils qw(deepvalue); +#use File::Which (); use Path::Class; -my $FN = Data::Hopen::Util::Filename->new; # for brevity -our $_CC; # Cached compiler name +#my $FN = Data::Hopen::Util::Filename->new; # for brevity +#our $_CC; # Cached compiler name # Docs {{{1 =head1 NAME -# App::hopen::T::Gnu::C::LinkCmd - link object files using the GNU toolset +App::hopen::T::Gnu::C::LinkCmd - link object files using the GNU toolset =head1 SYNOPSIS diff --git a/lib/App/hopen/TEMPLATE.pm b/lib/App/hopen/TEMPLATE.pm index 0f99a13..50f5704 100755 --- a/lib/App/hopen/TEMPLATE.pm +++ b/lib/App/hopen/TEMPLATE.pm @@ -1,9 +1,10 @@ # App::hopen::TEMPLATE - template for a hopen module package App::hopen::TEMPLATE; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; # TODO if using exporter use parent 'Exporter'; diff --git a/lib/App/hopen/Tool.pm b/lib/App/hopen/Tool.pm index f714133..a85406c 100755 --- a/lib/App/hopen/Tool.pm +++ b/lib/App/hopen/Tool.pm @@ -1,9 +1,10 @@ # App::hopen::Tool - base class for a hopen tool. DEPRECATED. package App::hopen::Tool; #use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use parent 'App::hopen::G::Cmd'; #use Class::Tiny; diff --git a/lib/App/hopen/Toolchain.pm b/lib/App/hopen/Toolchain.pm index f988461..a5ca951 100755 --- a/lib/App/hopen/Toolchain.pm +++ b/lib/App/hopen/Toolchain.pm @@ -1,9 +1,10 @@ # App::hopen::Toolchain - SUPERSEDED base class for hopen toolchains -package Data::Hopen::Toolchain; +package App::hopen::Toolchain; use Data::Hopen; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use Class::Tiny qw(proj_dir dest_dir), { architecture => '', @@ -13,7 +14,7 @@ use Class::Tiny qw(proj_dir dest_dir), { =head1 NAME -Data::Hopen::Toolchain - SUPERSEDED base class for hopen toolchains +App::hopen::Toolchain - SUPERSEDED base class for hopen toolchains =head1 SYNOPSIS @@ -36,7 +37,7 @@ Maybe TODO: hopen command line. The code that generates command lines to invoke specific toolchains lives under -C. Those modules must implement the interface defined +C. Those modules must implement the interface defined here. =head1 ATTRIBUTES @@ -52,7 +53,7 @@ should be written. =head1 FUNCTIONS -A toolchain (C subclass) is a Visitor. +A toolchain (C subclass) is a Visitor. TODO Figure out if the toolchain has access to L instances. diff --git a/lib/App/hopen/Util/BasedPath.pm b/lib/App/hopen/Util/BasedPath.pm index e4c32cb..3a6871f 100755 --- a/lib/App/hopen/Util/BasedPath.pm +++ b/lib/App/hopen/Util/BasedPath.pm @@ -1,8 +1,9 @@ # App::hopen::Util::BasedPath - A path relative to a specified base package App::hopen::Util::BasedPath; +use strict; use Data::Hopen::Base; -our $VERSION = '0.000010'; +our $VERSION = '0.000011'; use Exporter qw(import); our @EXPORT; BEGIN { @EXPORT = qw(based_path); } diff --git a/support/readme.pl b/support/readme.pl index b1f7e3c..1a97611 100755 --- a/support/readme.pl +++ b/support/readme.pl @@ -9,13 +9,16 @@ use Path::Class; # Parse command-line options -my ($source_fn, $dest_fn, $appveyor, $appveyor_badge); +my ($source_fn, $dest_fn, $appveyor, $appveyor_badge, $travis, $travis_badge); my $format = 'md'; GetOptions( "i|input=s" => \$source_fn, "o|output=s" => \$dest_fn, "f|format=s" => \$format, "appveyor=s" => \$appveyor, # username/repo - "avbadge=s" => \$appveyor_badge) # default $appveyor + "avbadge=s" => \$appveyor_badge, # default $appveyor + "travis=s" => \$travis, # username/repo + "trbadge=s" => \$travis_badge, # default $travis +) or die "Error in arguments. Usage:\nreadme_md.pl -i input -o output [-f format]\nFormat = md (default) or text."; die "Need an input file" unless $source_fn; @@ -23,7 +26,12 @@ $appveyor =~ m{^[A-Za-z0-9-]+/[A-Za-z0-9-]+} or die '--appveyor /' if $appveyor; $appveyor_badge //= $appveyor; -$appveyor_badge =~ m{^[A-Za-z0-9-]+/[A-Za-z0-9-]+} or die '--appveyor /' if $appveyor_badge; +$appveyor_badge =~ m{^[A-Za-z0-9-]+/[A-Za-z0-9-]+} or die '--avbadge /' if $appveyor_badge; + +$travis =~ m{^[A-Za-z0-9-]+/[A-Za-z0-9-]+} or die '--travis /' if $travis; +$travis_badge //= $travis; +$travis_badge =~ m{^[A-Za-z0-9-]+/[A-Za-z0-9-]+} or die '--trbadge /' if $travis_badge; + # Load the right parser my $parser; @@ -49,7 +57,6 @@ # Filter and tweak the POD my $saw_name = 0; my $tweak_name = ($format eq 'md'); -my $force_conventions = ($format eq 'md'); my $output = ''; while(my $line = <$fh>) { @@ -61,20 +68,21 @@ next; } elsif($tweak_name && $saw_name && $line =~ m{\H\h*$/}) { $output .= ($format eq 'md' ? '# ' : '') . "$line\n"; - $output .= "[![Appveyor Badge](https://ci.appveyor.com/api/projects/status/github/${appveyor_badge}?svg=true)](https://ci.appveyor.com/project/${appveyor})\n\n" if $appveyor; + $output .= "[![Appveyor Status](https://img.shields.io/appveyor/ci/${appveyor_badge}.svg?logo=appveyor)](https://ci.appveyor.com/project/${appveyor}) " if $appveyor; + $output .= "[![Travis Status](https://img.shields.io/travis/${travis_badge}.svg?logo=travis)](https://travis-ci.org/${travis}) " if $travis; + + $output .= "\n\n" if $appveyor || $travis; $saw_name = 0; next; } elsif($tweak_name && $saw_name) { next; # Waiting for the name line to come around } - next if $line =~ /SYNOPSIS/; # Don't need this header. + next if $line =~ /INTRODUCTION/; # Don't need this header. # Skip the internals - $output .= $line if $line =~ /OPTIONS/; - next if ($line =~ /INTERNALS/)..($line =~ /OPTIONS/); - - $line =~ s{https://metacpan.org/pod/App::hopen::Conventions}{https://github.com/hopenbuild/App-hopen/blob/master/lib/App/hopen/Conventions.pod} if $force_conventions; + $output .= $line if $line =~ /AUTHOR/; + next if ($line =~ /INTERNALS/)..($line =~ /AUTHOR/); $output .= $line; # Copy everything that's left. } diff --git a/t/020-dag.t b/t/020-dag.t deleted file mode 100644 index 85dc33d..0000000 --- a/t/020-dag.t +++ /dev/null @@ -1,32 +0,0 @@ -#!perl -# 020-dag.t: basic tests of Data::Hopen::G::DAG -use rlib 'lib'; -use HopenTest; - -BEGIN { - use_ok 'Data::Hopen::G::DAG'; - diag "Testing Data::Hopen::G::DAG from $INC{'Data/Hopen/G/DAG.pm'}"; -} - -my $dag = Data::Hopen::G::DAG->new(name=>'foo'); -isa_ok($dag, 'Data::Hopen::G::DAG'); -is($dag->name, 'foo', 'Name was set by constructor'); -$dag->name('bar'); -is($dag->name, 'bar', 'Name was set by accessor'); - -ok($dag->_graph, 'DAG has a _graph'); -ok($dag->_final, 'DAG has a _final'); - -my @goals; -foreach my $goalname (qw(all clean)) { - my $g1 = $dag->goal($goalname); - push @goals, $g1; - isa_ok($g1, 'Data::Hopen::G::Goal', 'DAG::goal()'); - is($g1->name, $goalname, 'DAG::goal() sets goal name'); - ok($dag->_graph->has_edge($g1, $dag->_final), 'DAG::goal() adds goal->final edge'); -} - -ok($dag->default_goal, 'DAG::goal() sets default_goal'); -is($dag->default_goal->name, 'all', 'First call to DAG::goal() sets default goal name'); - -done_testing(); diff --git a/t/900-eg-001.t b/t/900-eg-001.t new file mode 100755 index 0000000..942d142 --- /dev/null +++ b/t/900-eg-001.t @@ -0,0 +1,56 @@ +#!perl +# 900-eg-001.t: Test using eg/001. +use rlib 'lib'; +use HopenTest; + +use Capture::Tiny 'capture'; +use Path::Class; +use Test::Directory; + +use App::hopen; + +sub test_with { # Takes a generator + my $gen = shift or croak 'Need a generator'; + diag "Testing with -g $gen"; + + # Set up the dir and paths + my $dest = Test::Directory->new; + my $src = dir(qw(eg 001-single-file-hello)); # assume running in /, not /t + + # Check phase + my ($stdout, $stderr, $exitcode) = capture { + App::hopen::Main([ + '--fresh', + -g => $gen, + '--from', $src, + '--to', $dest->path('.'), + ]); + }; + + like $stdout, qr/\bCheck\b/, 'Ran "Check" phase'; + is $stderr, '', 'No error output' if $gen eq 'Make'; + cmp_ok $exitcode, '==', 0, 'Run succeeded'; + $dest->has('MY.hopen.pl'); + + # Gen phase + ($stdout, $stderr, $exitcode) = capture { + App::hopen::Main([ + -g => $gen, + '--from', $src, + '--to', $dest->path('.'), + ]); + }; + + like $stdout, qr/\bGen\b/, 'Ran "Gen" phase'; + is $stderr, '', 'No error output' if $gen eq 'Make'; + cmp_ok $exitcode, '==', 0, 'Run succeeded'; + $dest->has('MY.hopen.pl'); + $dest->has($gen eq 'Make' ? 'Makefile' : 'build.ninja'); + $dest->hasnt($gen eq 'Make' ? 'build.ninja' : 'Makefile'); + +} #test_with + +test_with 'Make'; +test_with 'Ninja'; + +done_testing(); diff --git a/t/lib/HopenTest.pm b/t/lib/HopenTest.pm index 0b6e665..ddb0ce5 100755 --- a/t/lib/HopenTest.pm +++ b/t/lib/HopenTest.pm @@ -20,6 +20,7 @@ use Carp; use Test::More; #use Text::ParseWords qw(shellwords); +use strict; use Data::Hopen::Base; BEGIN { $Data::Dumper::Indent = 1; } # For easier-to-read dumps diff --git a/xt/kwalitee.t b/xt/kwalitee.t new file mode 100755 index 0000000..f6726e1 --- /dev/null +++ b/xt/kwalitee.t @@ -0,0 +1,11 @@ +use Test::More; +use strict; +use warnings; +BEGIN { + plan skip_all => 'these tests are for release candidate testing' + unless $ENV{RELEASE_TESTING}; +} + +use Test::Kwalitee 'kwalitee_ok'; +kwalitee_ok(); +done_testing;