diff --git a/TODO b/TODO index 40bf136..702387f 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -### Looking for something to work on? See TASKS. +### Looking for something to work on? See TASKS. ### ### This file is more of a "don't forget to do this" list. @@ -141,7 +141,7 @@ Plumage: * Generate .pc files if module install type does not automatically do so * Hmmm, seems to be some fail here WRT standardizing lib names ... * Documentation - * Hacking docs (WIP) + * Add a tutorial/examples document * FAQ * Relationship between plumage and distutils/make/etc. * Test Suite @@ -152,7 +152,6 @@ Plumage: * Improve harness output * Misc suggestions * Merge Plumage .pbc's all together into executable using pbc_merge - * Make Plumage itself installable * Avoid asking questions (after first setup?) if at all possible * Recommendations ("optional dependencies") should not abort top-level install if they fail to install @@ -168,6 +167,26 @@ Plumage: * From #perl6: * mberends does not like adding directories to PATH mberends does 'sudo ln -s /bin/perl6 /usr/local/bin' instead +* Rename --ignore-fail==0 to --no-ignore-fail= as this is a more common + convention and is much cleaner + + +CLI: +---- +* Support temporary and permanent modification of config file +* Fully document usage in docs/interactive.pod +* Set version to 1.0 after soh-cah-toa/interactive is merged into master + * Display Plumage version in welcome message (update example in docs/interactive.pod) + + +Misc: +----- +* Move all perldoc to top of file so code is easier to read +* Improve output messages so that they don't try to sound like a real person talking + because this is really childish. +* Reword all comments that say Plumage is the "module ecosystem" because that's not + true: it's a package manager for the module ecosystem +* Cleanup code for probe tests Parrot: diff --git a/docs/interactive.pod b/docs/interactive.pod new file mode 100644 index 0000000..23bcf86 --- /dev/null +++ b/docs/interactive.pod @@ -0,0 +1,149 @@ +=begin pod + +=head1 Using the Plumage Interactive Command-Line Interface + +In addition to the basic single command interface from the shell, Plumage +also provides an interactive command-line interface (CLI). If you need to +run several commands, you will probably feel more comfortable with the CLI +rather than continually invoking Plumage for each command. + +The CLI is invoked by default when no commands have been specified. +Additionally, you can start an interactive session by explicitly specifying the +C command. However, this is a bit redundant so it's easier to just not +specify a command. + +=head2 Recognized Commands + +Any command that can be used from the shell interface can be invoked from the +CLI as well. The CLI also allows you to change the config file either just +temporarily or permanently. + +=head2 Examples + +After you've started Plumage, you will be greeted by the welcome message. + +=begin code + + Plumage: Package Manager for Parrot + Copyright (C) 2009-2011, Parrot Foundation. + + Enter 'help' for help or see docs/interactive.pod for further information. + + plumage> + +=end code + +As the above message suggests, if you aren't sure of something, use the C +command. + +=begin code + + plumage> help + List of commands: + + Query metadata and project info: + projects Lists all known projects. + status [] Shows status of (defaults to all). + info Displays detailed description of . + metadata Displays JSON metadata for . + show-deps Shows dependencies for . + project-dir Displays top directory for . + + Perform actions on a project: + fetch Downloads source code for . + update Updates source code for (falls back to fetch). + configure Configures source code for (runs 'update' first). + build Builds in current directory (runs 'configure' first). + test Runs test suite for (runs 'build' first). + smoke Sends test results to Parrot's Smolder server (runs 'build' first). + install Installs (runs 'test' first). + uninstall Uninstalls from system (not always available). + clean Performs basic cleanup of source tree for . + realclean Removes all generated files during the build process for . + + Get information about Plumage: + version Displays Plumage version and copyright statement. + help [] Displays a help message on usage (defaults to all). + +=end code + +Suppose you want to try a new programming language but aren't sure which. Let's +see what's available. + +=begin code + + plumage> projects + Known projects: + Close Systems programming language (C-like) for the Parrot VM + bf Brainfuck + blizkost Embed Perl 5 in Parrot, exposed as a standard HLL + cardinal Cardinal - Ruby compiler for Parrot + chitchat ChitChat + dbm-dynpmcs dynpmc interface to dbm-like databases. + decnum-dynpmcs Set of decimal arithmetic PMCs for the Parrot VM + digest-dynpmcs Set of message-digest dynpmcs for the Parrot VM. + ecmascript aka JavaScript + forth Forth on Parrot + fun An even happier Joy + gil Generic Imperative Language + hq9plus HQ9plus is a non turing-complete joke language + kakapo Run-time library for NQP programs on the Parrot VM + kea Factor on Parrot + lolcode Lolcode + lua Lua on Parrot + lua-batteries Libraries for Lua on Parrot + + # Output truncated for brevity + +=end code + +As you can see, the C command lists all the projects in the +ecosystem. Maybe you're feeling a little silly and want to write some +LOLCODE. Let's find out a little bit more about LOLCODE. + +=begin code + + plumage> info lolcode + Name : lolcode + Version : HEAD + Summary : Lolcode + Author : Parrot Foundation + URL : + License : Artistic License 2.0 + Description : Lolcode + +=end code + +Looks like someone was lazy and didn't fully fill out the LOLCODE metadata +file. That's alright, it'll be a surprise! + +=begin code + + plumage> install lolcode + /usr/bin/git clone git://github.com/parrot/lolcode.git /home/foo/.parrot/plumage/build/lolcode + Cloning into /home/foo/.parrot/plumage/build/lolcode... + /usr/bin/git submodule update --init + Successful. + + Configuration not required for lolcode. + Successful. + + # Output truncated for brevity + +=end code + +This builds, tests, and installs a particular project/module; in this case, +C. It could be that you're not really in a lolcatz mood right now. +Uninstalling a project is done with the aptly named C command. + +=begin code + + plumage> uninstall lolcode + + Uninstalling lolcode ... + /home/foo/bin/bin/parrot setup.pir uninstall + Successful. + +=end code + +=end pod diff --git a/man/man1/plumage.1 b/man/man1/plumage.1 index a855bef..e680362 100644 --- a/man/man1/plumage.1 +++ b/man/man1/plumage.1 @@ -13,78 +13,78 @@ ecosystem. .SH OPTIONS .TP \fB-h\fR, \fB--help\fR -Print a helpful usage message. +Displays a help message on command usage. .TP \fB\-c\fR, \fB\-\-config\-file\fR=\fIPATH\fR -Read additional config file. +Reads additional config file in \fIPATH\fR. .TP \fB-i\fR, \fB\-\-ignore\-fail\fR -Ignore any failing build stages. +Ignores any failed build stages. .TP \fB-i\fR, \fB\-\-ignore\-fail\fR=\fISTAGE\fR -Ignore failures only in a particular stage. May be repeated to select more than -one stage. +Ignores failures only for \fISTAGE\fR (may be repeated to select more than one +stage). .TP \fB-i\fR, \fB\-\-ignore\-fail\fR=\fISTAGE\fR=\fI0\fR -Don't ignore failues in this stage. +Doesn't ignore failures in \fISTAGE\fR. .SH COMMANDS -.SS Query metadata/project info: +.SS Query metadata and project info: .TP \fBprojects\fR -List all known projects. +Lists all known projects. .TP \fBstatus\fR [\fIPROJECT\fR] -Show status of projects. Defaults to all. +Shows status of \fIPROJECT\fR (defaults to all). .TP \fBinfo\fR \fIPROJECT\fR -Print summary about a particular project. +Displays detailed description of \fIPROJECT\fR. .TP \fBmetadata\fR \fIPROJECT\fR -Print JSON metadata about a particular project. +Displays JSON metadata for \fIPROJECT\fR. .TP -\fBshowdeps\fR \fIPROJECT\fR -Show dependency resolution for a project. +\fBshow-deps\fR \fIPROJECT\fR +Shows dependencies for \fIPROJECT\fR. .TP \fBproject-dir\fR \fIPROJECT\fR -Print project's top directory. +Displays top directory for \fIPROJECT\fR. .SS Perform actions on a project: .TP \fBfetch\fR \fIPROJECT\fR -Download source. +Downloads source code for \fIPROJECT\fR. .TP \fBupdate\fR \fIPROJECT\fR -Update source. Falls back to fetch. +Updates source code for \fIPROJECT\fR (falls back to fetch). .TP \fBconfigure\fR \fIPROJECT\fR -Configure source. Updates first. +Configures source code for \fIPROJECT\fR (runs 'update' first). .TP \fBbuild\fR \fIPROJECT\fR -Build project from source. Configures first. +Builds \fIPROJECT\fR in current directory (runs 'configure' first). .TP \fBtest\fR \fIPROJECT\fR -Test build project. Builds first. +Runs test suite for \fIPROJECT\fR (runs 'build' first). .TP \fBsmoke\fR \fIPROJECT\fR -Smoke test project. Builds first. +Sends test results to Parrot's Smolder server (runs 'build' first). .TP \fBinstall\fR \fIPROJECT\fR -Install built files. Tests first. +Installs \fIPROJECT\fR (runs 'test' first). .TP \fBuninstall\fR \fIPROJECT\fR -Uninstall installed files. Not always available. +Uninstalls \fIPROJECT\fR from system (not always available). .TP \fBclean\fR \fIPROJECT\fR -Clean source tree. +Performs basic cleanup of source tree for \fIPROJECT\fR. .TP \fBrealclean\fR \fIPROJECT\fR -Clobber/realclean source tree. +Removes all generated files during the build process for \fIPROJECT\fR. .SS Get info about Plumage itself: .TP \fBversion\fR -Print program version and copyright. +Displays Plumage version and copyright statement. .TP \fBhelp\fR [\fICOMMAND\fR] -Print a helpful usage message. +Displays a help message on \fICOMMAND\fR usage (defaults to all). .SH AUTHORS Written by Geoffrey Broadwell. .PP @@ -103,5 +103,4 @@ For further information, please see LICENSE or visit .UE . .SH SEE ALSO .PP -http://parrot.github.com/plumage - +http://parrot.github.com/plumage, https://github.com/parrot/plumage diff --git a/setup.pir b/setup.pir index 9993656..fa07ab7 100755 --- a/setup.pir +++ b/setup.pir @@ -19,77 +19,89 @@ No Configure step, no Makefile generated. .sub 'main' :main .param pmc args + $S0 = shift args load_bytecode 'distutils.pbc' + .local pmc config config = get_config() - .const 'Sub' selfinstall = 'selfinstall' - register_step('selfinstall', selfinstall) - - $P0 = new 'Hash' - $P0['name'] = 'Plumage' - $P0['abstract'] = 'Parrot Plumage is the Parrot module ecosystem' - $P0['authority'] = 'http://github.com/parrot' - $P0['description'] = 'Parrot Plumage is the Parrot module ecosystem. It includes tools to search metadata, handle dependencies, install modules, and so forth.' - $P1 = split ',', 'parrot package deployment module ecosystem' - $P0['keywords'] = $P1 - $P0['license_type'] = 'Artistic License 2.0' - $P0['license_uri'] = 'http://www.perlfoundation.org/artistic_license_2_0' - $P0['copyright_holder'] = 'Parrot Foundation' - $P0['checkout_uri'] = 'git://github.com/parrot/plumage.git' - $P0['browser_uri'] = 'http://github.com/parrot/plumage' - $P0['project_uri'] = 'https://trac.parrot.org/parrot/wiki/ModuleEcosystem' - - # build - $P2 = new 'Hash' - $P2['src/lib/Plumage/Dependencies.pir'] = 'src/lib/Plumage/Dependencies.nqp' - $P2['src/lib/Plumage/Metadata.pir'] = 'src/lib/Plumage/Metadata.nqp' - $P2['src/lib/Plumage/Project.pir'] = 'src/lib/Plumage/Project.nqp' - $P2['src/lib/Plumage/Util.pir'] = 'src/lib/Plumage/Util.nqp' - $P2['src/lib/Plumage/NQPUtil.pir'] = 'src/lib/Plumage/NQPUtil.nqp' - $P2['src/plumage.pir'] = 'src/plumage.nqp' - $P0['pir_nqp'] = $P2 - - $P3 = new 'Hash' - $P3['Plumage/Dependencies.pbc'] = 'src/lib/Plumage/Dependencies.pir' - $P3['Plumage/Metadata.pbc'] = 'src/lib/Plumage/Metadata.pir' - $P3['Plumage/Project.pbc'] = 'src/lib/Plumage/Project.pir' - $P3['Plumage/Util.pbc'] = 'src/lib/Plumage/Util.pir' - $P3['Plumage/NQPUtil.pbc'] = 'src/lib/Plumage/NQPUtil.pir' - $P3['plumage.pbc'] = 'src/plumage.pir' - $P0['pbc_pir'] = $P3 - - $P4 = new 'Hash' - $P4['plumage'] = 'plumage.pbc' + .const 'Sub' selfinstall = 'selfinstall' + register_step('selfinstall', selfinstall) + + $S1 = 'Plumage is a package manager for the Parrot VM module ecosystem. ' + $S1 .= 'With it, you can perform tasks such as searching, installing, and ' + $S1 .= 'testing modules in the ecosystem.' + + $P0 = new 'Hash' + $P0['name'] = 'Plumage' + $P0['abstract'] = 'Plumage is a package manager for the Parrot VM module ecosystem.' + $P0['authority'] = 'http://github.com/parrot' + $P0['description'] = $S1 + $P1 = split ',', 'parrot package deployment module ecosystem' + $P0['keywords'] = $P1 + $P0['license_type'] = 'Artistic License 2.0' + $P0['license_uri'] = 'http://www.perlfoundation.org/artistic_license_2_0' + $P0['copyright_holder'] = 'Parrot Foundation' + $P0['checkout_uri'] = 'git://github.com/parrot/plumage.git' + $P0['browser_uri'] = 'https://github.com/parrot/plumage' + $P0['project_uri'] = 'https://trac.parrot.org/parrot/wiki/ModuleEcosystem' + + # Build + $P2 = new 'Hash' + $P2['src/lib/Plumage/Dependencies.pir'] = 'src/lib/Plumage/Dependencies.nqp' + $P2['src/lib/Plumage/Metadata.pir'] = 'src/lib/Plumage/Metadata.nqp' + $P2['src/lib/Plumage/Project.pir'] = 'src/lib/Plumage/Project.nqp' + $P2['src/lib/Plumage/Util.pir'] = 'src/lib/Plumage/Util.nqp' + $P2['src/lib/Plumage/NQPUtil.pir'] = 'src/lib/Plumage/NQPUtil.nqp' + $P2['src/lib/Plumage/Interactive.pir'] = 'src/lib/Plumage/Interactive.nqp' + $P2['src/lib/Plumage/Command.pir'] = 'src/lib/Plumage/Command.nqp' + $P2['src/plumage.pir'] = 'src/plumage.nqp' + $P0['pir_nqp'] = $P2 + + $P3 = new 'Hash' + $P3['Plumage/Dependencies.pbc'] = 'src/lib/Plumage/Dependencies.pir' + $P3['Plumage/Metadata.pbc'] = 'src/lib/Plumage/Metadata.pir' + $P3['Plumage/Project.pbc'] = 'src/lib/Plumage/Project.pir' + $P3['Plumage/Util.pbc'] = 'src/lib/Plumage/Util.pir' + $P3['Plumage/NQPUtil.pbc'] = 'src/lib/Plumage/NQPUtil.pir' + $P3['Plumage/Interactive.pbc'] = 'src/lib/Plumage/Interactive.pir' + $P3['Plumage/Command.pbc'] = 'src/lib/Plumage/Command.pir' + $P3['plumage.pbc'] = 'src/plumage.pir' + $P0['pbc_pir'] = $P3 + + $P4 = new 'Hash' + $P4['plumage'] = 'plumage.pbc' $P0['installable_pbc'] = $P4 - # test - $S0 = get_nqp_rx() + # Test + $S0 = get_nqp_rx() $P0['prove_exec'] = $S0 - # smoke + # Smoke test $P0['prove_archive'] = 'test_plumage.tar.gz' - $P0['smolder_url'] = 'http://smolder.parrot.org/app/projects/process_add_report/3' -# $P0['smolder_comments'] = 'plumage' - $S0 = get_tags(config) - $P0['smolder_tags'] = $S0 + $P0['smolder_url'] = 'http://smolder.parrot.org/app/projects/process_add_report/3' + $S0 = get_tags(config) + $P0['smolder_tags'] = $S0 - # install + # Install $P5 = split "\n", <<'LIBS' Plumage/Dependencies.pbc Plumage/Metadata.pbc Plumage/Project.pbc Plumage/Util.pbc Plumage/NQPUtil.pbc +Plumage/Interactive.pbc +Plumage/Command.pbc LIBS - $S0 = pop $P5 - $P0['inst_lib'] = $P5 - $P6 = glob('plumage/metadata/*.json') + + $S0 = pop $P5 + $P0['inst_lib'] = $P5 + $P6 = glob('plumage/metadata/*.json') $P0['inst_data'] = $P6 - # dist - $P7 = glob('CREDITS README TASKS TODO docs/*/*.pod') + # Distribution + $P7 = glob('CREDITS README TASKS TODO docs/*/*.pod') $P0['doc_files'] = $P7 .tailcall setup(args :flat, $P0 :flat :named) @@ -97,20 +109,22 @@ LIBS .sub 'selfinstall' :anon .param pmc kv :slurpy :named + system('parrot plumage.pbc install plumage', 1 :named('verbose')) .end .sub 'get_tags' .param pmc config + .local string tags - tags = config['osname'] + tags = config['osname'] tags .= ", " - $S0 = config['archname'] + $S0 = config['archname'] tags .= $S0 + .return (tags) .end - # Local Variables: # mode: pir # fill-column: 100 diff --git a/src/lib/Plumage/Command.nqp b/src/lib/Plumage/Command.nqp new file mode 100644 index 0000000..5d073d5 --- /dev/null +++ b/src/lib/Plumage/Command.nqp @@ -0,0 +1,76 @@ +# Copyright (C) 2011, Parrot Foundation. + +=begin pod + +=head1 NAME + +Plumage::Command - represents a Plumage command + +=head1 DESCRIPTION + +The C class is an abstract representation of a Plumage +command. It keeps track of the command name, its arguments, and any associated +help/usage information. + +=head2 Object Initialization + +=item C + +Returns a new C instance. + +=head2 Public Attributes + +=item C<$!action> + +Contains a reference to the callback subroutine to execute. + +=item C<$!args> + +A string representing the type of arguments the command takes. It can take the +following forms: + + * 'none' - no arguments + * 'opt_command' - optional command + * 'opt_project' - optional project + * 'project' - project name + +=item C<$!usage> + +A string which describes the semantics of using the command. + +=item C<$!help> + +A string which describes the purpose of the command in more detail. + +=end pod + +class Plumage::Command; + +has $!action; # Subroutine to execute +has $!args; # Type of argument(s) command takes +has $!usage; # Describes semantics of command usage +has $!help; # Describes purpose of command in more detail + +# Accessors +method action() { $!action } +method args() { $!args } +method usage() { $!usage } +method help() { $!help } + +method new(:$action, :$args, :$usage, :$help) { + my $class := pir::getattribute__PPs(self.HOW, "parrotclass"); + + Q:PIR { + $P0 = find_lex '$class' + self = new $P0 + }; + + $!action := $action; + $!args := $args; + $!usage := $usage; + $!help := $help; + + return self; +} + +# vim: ft=perl6 diff --git a/src/lib/Plumage/Dependencies.nqp b/src/lib/Plumage/Dependencies.nqp index ab9a13a..991ab84 100644 --- a/src/lib/Plumage/Dependencies.nqp +++ b/src/lib/Plumage/Dependencies.nqp @@ -1,3 +1,5 @@ +# Copyright (C) 2009-2011, Parrot Foundation. + =begin =head1 NAME @@ -9,16 +11,13 @@ Plumage::Dependencies - Resolve dependency relationships # Load this library pir::load_bytecode('Plumage/Dependencies.pbc'); - - =head1 DESCRIPTION =end class Plumage::Dependencies; - -method resolve_dependencies (@projects) { +method resolve_dependencies(@projects) { my @known_projects := Plumage::Metadata.get_project_list(); my @all_deps := self.all_dependencies(@projects); my @installed := self.get_installed_projects; @@ -61,8 +60,7 @@ method resolve_dependencies (@projects) { return %resolutions; } - -method all_dependencies (@projects) { +method all_dependencies(@projects) { my @dep_stack; my @deps; my %seen; @@ -99,8 +97,7 @@ method all_dependencies (@projects) { return @deps; } - -method get_installed_projects () { +method get_installed_projects() { my $inst_file := replace_config_strings(%*CONF); my $contents := slurp($inst_file); my @projects := grep(-> $_ { ?$_ }, pir::split("\n", $contents)); @@ -112,16 +109,14 @@ method get_installed_projects () { } } - -method mark_projects_installed (@projects) { +method mark_projects_installed(@projects) { my $lines := pir::join("\n", @projects) ~ "\n"; my $inst_file := replace_config_strings(%*CONF); append($inst_file, $lines); } - -method mark_projects_uninstalled (@projects) { +method mark_projects_uninstalled(@projects) { my %uninst := set_from_array(@projects); my $inst_file := replace_config_strings(%*CONF); my $contents := slurp($inst_file); @@ -131,3 +126,5 @@ method mark_projects_uninstalled (@projects) { spew($inst_file, $lines); } + +# vim: ft=perl6 diff --git a/src/lib/Plumage/Interactive.nqp b/src/lib/Plumage/Interactive.nqp new file mode 100644 index 0000000..2d53321 --- /dev/null +++ b/src/lib/Plumage/Interactive.nqp @@ -0,0 +1,92 @@ +# Copyright (C) 2011, Parrot Foundation. + +=begin pod + +=head1 NAME + +Plumage::Interactive - manages an interactive session + +=head1 DESCRIPTION + +The C class handles all the tasks of managing the +interactive command-line interface (CLI); for example, prompting for commands, +parsing user input, and running commands. + +=head2 Object Initialization + +=item C + +Returns a new C instance. + +=head2 Public Attributes + +=item C<$!input> + +Contains the user input string. + +=item C<$!prompt_string> + +A string representing the text used for the command prompt. Defaults to +I. + +=head2 Methods + +=item C + +Splits the user input in C<$!input> and returns an array where the first +element is the command and the remainder contains the arguments (if any). + +=item C + +Displays the command prompt and blocks until the user enters something. + +=item C + +Displays the welcome message. Called only once when the C +object is instantiated. + +=end pod + +# TODO Make Plumage::Interactive a singleton object + +class Plumage::Interactive; + +has $!input; # User input +has $!prompt_string; # Text used for command prompt + +# Accessors +method input() { $!input } +method prompt_string() { $!prompt_string } + +method new(:$prompt_string = 'plumage') { + my $class := pir::getattribute__PPs(self.HOW, "parrotclass"); + + Q:PIR { + $P0 = find_lex '$class' + self = new $P0 + }; + + $!prompt_string := $prompt_string; + + self.welcome(); + + return self; +} + +method parse_command_line() { + pir::split__Pss(' ', $!input); +} + +method prompt() { + $!input := pir::getstdin__P().readline_interactive("$!prompt_string> "); +} + +method welcome() { + say("Plumage: Package Manager for Parrot\n" + ~ "Copyright (C) 2009-2011, Parrot Foundation.\n\n" + + ~ "Enter 'help' for help or see docs/interactive.pod " + ~ "for further information.\n"); +} + +# vim: ft=perl6 diff --git a/src/lib/Plumage/Metadata.nqp b/src/lib/Plumage/Metadata.nqp index 759e547..b736227 100644 --- a/src/lib/Plumage/Metadata.nqp +++ b/src/lib/Plumage/Metadata.nqp @@ -1,3 +1,5 @@ +# Copyright (C) 2009-2011, Parrot Foundation. + =begin =head1 NAME @@ -31,14 +33,12 @@ Plumage::Metadata - Project metadata: find it, parse it, query it $meta.save_install_copy; $meta.remove_install_copy; - =head1 DESCRIPTION =end class Plumage::Metadata; - =begin =head2 Class Methods @@ -53,7 +53,7 @@ passing to C<$meta.find_by_project_name()> to obtain more details. =end -method get_project_list () { +method get_project_list() { my @files := $*OS.readdir(replace_config_strings(%*CONF)); my $regex := /\.json$/; my @projects; @@ -68,7 +68,6 @@ method get_project_list () { return @projects; } - =begin =item $found := Plumage::Metadata.exists($project_directory?) @@ -80,11 +79,10 @@ location for Plumage metadata files. =end -method exists ($dir?) { +method exists($dir?) { return path_exists(self.project_metadata_path($dir)); } - =begin =item $path := Plumage::Metadata.project_metadata_path($project_directory?) @@ -96,7 +94,7 @@ the C<$path> will be the generic default location relative to a project root. =end -method project_metadata_path ($dir?) { +method project_metadata_path($dir?) { my @dir := pir::length($dir) ?? [$dir, 'plumage'] !! [ 'plumage']; my $path := fscat(@dir, 'metadata.json'); @@ -104,7 +102,6 @@ method project_metadata_path ($dir?) { return $path; } - =begin =item $meta := Plumage::Metadata.new @@ -113,7 +110,6 @@ Instantiate a new, empty metadata object. =back - =head2 Accessors =over 4 @@ -140,10 +136,9 @@ has %!metadata; has $!valid; has $!error; -method is_valid () { ?$!valid } -method error () { $!error } -method metadata () { %!metadata } - +method is_valid() { ?$!valid } +method error() { $!error } +method metadata() { %!metadata } =begin @@ -165,7 +160,7 @@ C. =end -method find_by_project_name ($project_name) { +method find_by_project_name($project_name) { my $meta_dir := replace_config_strings(%*CONF); my $meta_file := fscat([$meta_dir], "$project_name.json"); @@ -178,7 +173,6 @@ method find_by_project_name ($project_name) { return self.load_from_file($meta_file); } - =begin =item $valid := $meta.load_from_project_dir($project_directory) @@ -189,11 +183,10 @@ C). =end -method load_from_project_dir ($dir) { +method load_from_project_dir($dir) { return self.load_from_file(self.project_metadata_path($dir)); } - =begin =item $valid := $meta.load_from_file($metadata_file_path) @@ -202,7 +195,7 @@ Load and parse a particular metadata file. =end -method load_from_file ($path) { +method load_from_file($path) { %!metadata := Config::JSON::ReadConfig($path); return self.validate; @@ -214,7 +207,6 @@ method load_from_file ($path) { } } - =begin =item $valid := $meta.load_from_string($serialized_metadata) @@ -235,7 +227,7 @@ method load_from_string($serialized) { } } -method validate () { +method validate() { $!valid := %!metadata && self.metadata_spec_known && self.metadata_instruction_types_known; @@ -245,7 +237,7 @@ method validate () { return $!valid; } -method metadata_spec_known () { +method metadata_spec_known() { my %spec := %!metadata; my $known_uri := 'https://trac.parrot.org/parrot/wiki/ModuleEcosystem'; my $known_version := 1; @@ -276,7 +268,7 @@ method metadata_spec_known () { return 0; } -method metadata_instruction_types_known () { +method metadata_instruction_types_known() { my %inst := %!metadata; unless %inst && %inst.keys { @@ -313,7 +305,6 @@ method metadata_instruction_types_known () { return 1; } - =begin =head2 Saved Copy @@ -326,7 +317,7 @@ Return the file path for the metadata copy saved after install. =end -method saved_copy_path () { +method saved_copy_path() { my $meta_root := _saved_copy_root(); my $copy_path := fscat([$meta_root], pir::downcase(%!metadata) ~ '.json'); @@ -334,11 +325,10 @@ method saved_copy_path () { return $copy_path; } -sub _saved_copy_root () { +sub _saved_copy_root() { return replace_config_strings(%*CONF); } - =begin =item $meta.save_install_copy() @@ -349,13 +339,12 @@ installed. =end -method save_install_copy () { +method save_install_copy() { mkpath(_saved_copy_root()); Config::JSON::WriteConfig(%!metadata, self.saved_copy_path); } - =begin =item $meta.remove_install_copy() @@ -365,15 +354,16 @@ because the associated project was successfully uninstalled. =end -method remove_install_copy () { +method remove_install_copy() { my $path := self.saved_copy_path; $*OS.rm($path) if path_exists($path); } - =begin =back =end + +# vim: ft=perl6 diff --git a/src/lib/Plumage/NQPUtil.nqp b/src/lib/Plumage/NQPUtil.nqp index 4c0454e..d147dd2 100644 --- a/src/lib/Plumage/NQPUtil.nqp +++ b/src/lib/Plumage/NQPUtil.nqp @@ -1,3 +1,5 @@ +# Copyright (C) 2009-2011, Parrot Foundation. + =begin =head1 NAME @@ -68,7 +70,6 @@ Plumage::NQPUtil.nqp - Utility functions for NQP my %*VM; my $*OS; - =head1 DESCRIPTION =head2 Hash Methods @@ -80,7 +81,6 @@ functionality expected for Perl 6 Hashes. module Hash { - =begin =over 4 @@ -91,7 +91,7 @@ Return a true value if C<$key> exists in C<%hash>, or a false value otherwise. =end - method exists ($key) { + method exists($key) { return Q:PIR{ $P1 = find_lex '$key' $I0 = exists self[$P1] @@ -99,7 +99,6 @@ Return a true value if C<$key> exists in C<%hash>, or a false value otherwise. }; } - =begin =item @keys := %hash.keys @@ -108,13 +107,12 @@ Return all the C<@keys> in the C<%hash> as an unordered array. =end - method keys () { + method keys() { my @keys; for self { @keys.push($_.key); } @keys; } - =begin =item @values := %hash.values @@ -123,13 +121,12 @@ Return all the C<@values> in the C<%hash> as an unordered array. =end - method values () { + method values() { my @values; for self { @values.push($_.value); } @values; } - =begin =item @flattened := %hash.kv @@ -141,13 +138,12 @@ when iterating over key and value simultaneously: =end - method kv () { + method kv() { my @kv; for self { @kv.push($_.key); @kv.push($_.value); } @kv; } - =begin =back @@ -156,7 +152,6 @@ when iterating over key and value simultaneously: } - =begin =head2 Array Methods @@ -168,7 +163,6 @@ functionality expected for Perl 6 Hashes. module Array { - =begin =over 4 @@ -179,13 +173,12 @@ Return a C<@reversed> copy of the C<@array>. =end - method reverse () { + method reverse() { my @reversed; for self { @reversed.unshift($_); } @reversed; } - =begin =back @@ -194,7 +187,6 @@ Return a C<@reversed> copy of the C<@array>. } - =begin =head2 Basic Functions @@ -213,7 +205,7 @@ one entry in the C<@mapped> output. =end -sub map (&code, @originals) { +sub map(&code, @originals) { my @mapped; for @originals { @@ -223,7 +215,6 @@ sub map (&code, @originals) { return @mapped; } - =begin =item @matches := grep(&code, @all) @@ -233,7 +224,7 @@ Order is retained, and duplicates are handled independently. =end -sub grep (&code, @all) { +sub grep(&code, @all) { my @matches; for @all { @@ -243,7 +234,6 @@ sub grep (&code, @all) { return @matches; } - =begin =item $result := reduce(&code, @array, $initial?) @@ -265,7 +255,7 @@ C<$result> is an undefined value. =end -sub reduce (&code, @array, *@initial) { +sub reduce(&code, @array, *@initial) { my $init_elems := pir::elements(@initial); if $init_elems > 1 { pir::die('Only one initial value allowed in reduce()'); @@ -303,7 +293,6 @@ sub _reduce(&code, $iter, $initial) { return $result; } - =begin =head2 Container Coercions @@ -321,8 +310,7 @@ Coerce a list of pairs into a hash. =end -sub hash (*%h) { return %h } - +sub hash(*%h) { return %h } =begin @@ -334,7 +322,7 @@ checks. =end -sub set_from_array (@array) { +sub set_from_array(@array) { my %set; for @array { @@ -344,12 +332,10 @@ sub set_from_array (@array) { return %set; } - =begin =back - =head2 Regular Expression Functions These functions add more power to the basic regex matching capability, @@ -373,7 +359,6 @@ sub all_matches($regex, $text) { return @matches; } - =begin =item $edited := subst($original, $regex, $replacement) @@ -419,12 +404,10 @@ sub subst($original, $regex, $replacement) { return $edited; } - =begin =back - =head2 I/O Functions Basic stdio and file I/O functions. @@ -437,13 +420,12 @@ Print a list of strings to standard output. =end -sub print (*@strings) { +sub print(*@strings) { for @strings { pir::print($_); } } - =begin =item say('things', ' to ', 'say', ...) @@ -452,11 +434,10 @@ Print a list of strings to standard output, followed by a newline. =end -sub say (*@strings) { +sub say(*@strings) { print(|@strings, "\n"); } - =begin =item $contents := slurp($filename) @@ -465,7 +446,7 @@ Read the C<$contents> of a file as a single string. =end -sub slurp ($filename) { +sub slurp($filename) { my $fh := pir::new__Ps('FileHandle'); $fh.open($filename, 'r'); my $contents := $fh.readall; @@ -474,7 +455,6 @@ sub slurp ($filename) { return $contents; } - =begin =item spew($filename, $contents) @@ -483,14 +463,13 @@ Write the string C<$contents> to a file. =end -sub spew ($filename, $contents) { +sub spew($filename, $contents) { my $fh := pir::new__Ps('FileHandle'); $fh.open($filename, 'w'); $fh.print($contents); $fh.close(); } - =begin =item append($filename, $contents) @@ -499,19 +478,17 @@ Append the string C<$contents> to a file. =end -sub append ($filename, $contents) { +sub append($filename, $contents) { my $fh := pir::new__Ps('FileHandle'); $fh.open($filename, 'a'); $fh.print($contents); $fh.close(); } - =begin =back - =head2 Filesystem and Path Functions These functions provide convenient ways to interact with the file system, @@ -539,7 +516,6 @@ sub fscat(@path_parts, *@filename) { return $joined; } - =begin =item $home := user_home_dir() @@ -553,7 +529,6 @@ sub user_home_dir() { return (%env // '') ~ %env; } - =begin =item $found := path_exists($path); @@ -563,7 +538,7 @@ value if not. =end -sub path_exists ($path) { +sub path_exists($path) { my @stat := pir::root_new__PP(< parrot OS >).stat($path); return 1; @@ -572,7 +547,6 @@ sub path_exists ($path) { } } - =begin =item $is_dir := is_dir($path); @@ -591,7 +565,6 @@ sub is_dir($path) { } } - =begin =item $writable := test_dir_writable($directory_path) @@ -628,7 +601,6 @@ sub test_dir_writable($dir) { } } - =begin =item $binary_path := find_program($program) @@ -648,7 +620,7 @@ following way: =end -sub find_program ($program) { +sub find_program($program) { my $path_sep := %*VM eq 'MSWin32' ?? ';' !! ':'; my %env := pir::root_new__PP(< parrot Env >); my @paths := pir::split($path_sep, %env); @@ -668,7 +640,6 @@ sub find_program ($program) { return ''; } - =begin =item mkpath($directory_path) @@ -678,7 +649,7 @@ top making directories as needed until an entire path has been created. =end -sub mkpath ($path) { +sub mkpath($path) { my @path := pir::split('/', $path); my $cur := @path.shift; @@ -691,12 +662,10 @@ sub mkpath ($path) { } } - =begin =back - =head2 Program Spawning Functions These functions provide several variations on the "spawn a child program @@ -713,7 +682,7 @@ if the process could not be spawned at all. =end -sub run (*@command_and_args) { +sub run(*@command_and_args) { my $aux := pir::spawnw__iP(@command_and_args); my $ret := Q:PIR< $P0 = find_lex '$aux' @@ -724,7 +693,6 @@ sub run (*@command_and_args) { return $ret; } - =begin =item $success := do_run($command, $and, $args, ...) @@ -738,7 +706,7 @@ successfully but itself exited with failure. =end -sub do_run (*@command_and_args) { +sub do_run(*@command_and_args) { say(pir::join(' ', @command_and_args)); return pir::spawnw__iP(@command_and_args) ?? 0 !! 1; @@ -748,7 +716,6 @@ sub do_run (*@command_and_args) { } } - =begin =item $output := qx($command, $and, $args, ...) @@ -762,7 +729,7 @@ B: Parrot currently implements the pipe open B! =end -sub qx (*@command_and_args) { +sub qx(*@command_and_args) { my $cmd := pir::join(' ', @command_and_args); my $pipe := pir::new__Ps('FileHandle'); $pipe.open($cmd, 'rp'); @@ -777,12 +744,10 @@ sub qx (*@command_and_args) { return $output; } - =begin =back - =head2 HLL Interop Functions These functions allow code in other languages to be evaluated and the @@ -797,7 +762,7 @@ returning the C<$result> of executing the compiled code. =end -sub eval ($source_code, $language) { +sub eval($source_code, $language) { $language := pir::downcase($language); pir::load_language($language); @@ -806,7 +771,6 @@ sub eval ($source_code, $language) { return $compiler.compile($source_code)(); } - =begin =head2 Deep Magic @@ -832,12 +796,10 @@ sub store_dynlex_safely($var_name, $value) { unless pir::isnull(pir::find_dynamic_lex($var_name)); } - =begin =back - =head2 Global Variables Standard variables available in Perl 6, variously known as "core globals", @@ -911,3 +873,5 @@ INIT { store_dynlex_safely('%*ENV', pir::root_new__PP(< parrot Env >)); store_dynlex_safely('$*OS', pir::root_new__PP(< parrot OS >)); } + +# vim: ft=perl6 diff --git a/src/lib/Plumage/Project.nqp b/src/lib/Plumage/Project.nqp index 8d61b94..4d82ec9 100644 --- a/src/lib/Plumage/Project.nqp +++ b/src/lib/Plumage/Project.nqp @@ -1,3 +1,5 @@ +# Copyright (C) 2009-2011, Parrot Foundation. + =begin =head1 NAME @@ -33,7 +35,6 @@ Plumage::Project - A project, its metadata, and its state $project.clean; $project.realclean; - =head1 DESCRIPTION =end @@ -45,10 +46,9 @@ has $!name; has $!metadata; has $!source_dir; -method name () { $!name } -method metadata () { $!metadata } -method source_dir () { $!source_dir } - +method name() { $!name } +method metadata() { $!metadata } +method source_dir() { $!source_dir } # CONSTRUCTION @@ -60,7 +60,6 @@ method new($locator) { return self._init($locator); } - method _init($locator) { $!metadata := Plumage::Metadata.new; my $build_root := replace_config_strings(%*CONF); @@ -97,7 +96,6 @@ method _init($locator) { return self; } - method _find_source_dir($start_dir?) { my $orig_dir := $*OS.cwd; @@ -123,18 +121,17 @@ sub _get_winxed() { return "$parrot_bin/winxed"; } - ### ### ACTIONS ### -method known_actions () { +method known_actions() { return grep(-> $_ {self.HOW.can(self, $_)}, < fetch update configure build test smoke install uninstall clean realclean >); } -sub _build_stage_paths () { +sub _build_stage_paths() { our %STAGES; # All stages in install path require their predecessors @@ -152,14 +149,14 @@ sub _build_stage_paths () { %STAGES[-1] := 'smoke'; } -method _actions_up_to ($stage) { +method _actions_up_to($stage) { our %STAGES; _build_stage_paths(); return %STAGES{$stage}; } -method perform_actions (:$up_to, :@actions, :$ignore_all, :%ignore) { +method perform_actions(:$up_to, :@actions, :$ignore_all, :%ignore) { if $up_to && @actions { die("Cannot specify both up_to and actions in perform_actions()"); } @@ -197,10 +194,9 @@ method perform_actions (:$up_to, :@actions, :$ignore_all, :%ignore) { return 1; } - # FETCH -method fetch () { +method fetch() { my %fetch := $!metadata.metadata; if %fetch { my $build_root := replace_config_strings(%*CONF); @@ -216,7 +212,7 @@ method fetch () { } } -method fetch_repository () { +method fetch_repository() { my %repo := $!metadata.metadata; if %repo { say("Fetching $!name ..."); @@ -229,7 +225,7 @@ method fetch_repository () { } } -method fetch_git () { +method fetch_git() { if path_exists($!source_dir) { if path_exists(fscat([$!source_dir, '.git'])) { $*OS.chdir($!source_dir); @@ -250,7 +246,7 @@ method fetch_git () { } } -method fetch_hg () { +method fetch_hg() { if path_exists($!source_dir) { if path_exists(fscat([$!source_dir, '.hg'])) { $*OS.chdir($!source_dir); @@ -267,7 +263,7 @@ method fetch_hg () { } } -method fetch_svn () { +method fetch_svn() { if path_exists($!source_dir) && !path_exists(fscat([$!source_dir, '.svn'])) { return report_fetch_collision('Subversion'); @@ -279,7 +275,7 @@ method fetch_svn () { } } -method report_fetch_collision ($type) { +method report_fetch_collision($type) { say("\n$!name is a $type project, but the fetch directory:\n" ~ "\n $!source_dir\n\n" ~ "already exists and is not the right type.\n" @@ -288,10 +284,9 @@ method report_fetch_collision ($type) { return 0; } - # UPDATE -method update () { +method update() { my %update := $!metadata.metadata; if %update && path_exists($!source_dir) { @@ -303,7 +298,7 @@ method update () { } } -method update_repository () { +method update_repository() { my %repo := $!metadata.metadata; if %repo { say("Updating $!name ..."); @@ -317,13 +312,13 @@ method update_repository () { } } -method update_parrot_setup () { +method update_parrot_setup() { $*OS.chdir($!source_dir); return do_run(%*BIN, 'setup.pir', 'update'); } -method update_nqp_setup () { +method update_nqp_setup() { $*OS.chdir($!source_dir); return do_run(%*BIN, 'setup.nqp', 'update'); @@ -335,10 +330,9 @@ method update_winxed_setup() { return do_run(_get_winxed(), 'setup.winxed', 'update'); } - # CONFIGURE -method configure () { +method configure() { my %conf := $!metadata.metadata; if %conf { say("\nConfiguring $!name ..."); @@ -353,29 +347,28 @@ method configure () { } } -method configure_rake () { +method configure_rake() { return do_run(%*BIN, 'config'); } -method configure_perl5_configure () { +method configure_perl5_configure() { my $extra := $!metadata.metadata; my @extra := map(replace_config_strings, $extra); return do_run(%*BIN, 'Configure.pl', |@extra); } -method configure_parrot_configure () { +method configure_parrot_configure() { return do_run(%*BIN, 'Configure.pir'); } -method configure_nqp_configure () { +method configure_nqp_configure() { return do_run(%*BIN, 'Configure.nqp'); } - # BUILD -method build () { +method build() { my %build := $!metadata.metadata; if %build { say("\nBuilding $!name ..."); @@ -390,19 +383,19 @@ method build () { } } -method build_make () { +method build_make() { return do_run(%*BIN); } -method build_rake () { +method build_rake() { return do_run(%*BIN); } -method build_parrot_setup () { +method build_parrot_setup() { return do_run(%*BIN, 'setup.pir'); } -method build_nqp_setup () { +method build_nqp_setup() { return do_run(%*BIN, 'setup.nqp'); } @@ -410,10 +403,9 @@ method build_winxed_setup() { return do_run(_get_winxed(), 'setup.winxed', 'build'); } - # TEST -method test () { +method test() { my %test := $!metadata.metadata; if %test { say("\nTesting $!name ..."); @@ -428,19 +420,19 @@ method test () { } } -method test_make () { +method test_make() { return do_run(%*BIN, 'test'); } -method test_rake () { +method test_rake() { return do_run(%*BIN, 'test'); } -method test_parrot_setup () { +method test_parrot_setup() { return do_run(%*BIN, 'setup.pir', 'test'); } -method test_nqp_setup () { +method test_nqp_setup() { return do_run(%*BIN, 'setup.nqp', 'test'); } @@ -448,10 +440,9 @@ method test_winxed_setup() { return do_run(_get_winxed(), 'setup.winxed', 'test'); } - # SMOKE -method smoke () { +method smoke() { my %smoke := $!metadata.metadata; if %smoke { say("\nSmoke testing $!name ..."); @@ -466,15 +457,15 @@ method smoke () { } } -method smoke_make () { +method smoke_make() { return do_run(%*BIN, 'smoke'); } -method smoke_parrot_setup () { +method smoke_parrot_setup() { return do_run(%*BIN, 'setup.pir', 'smoke'); } -method smoke_nqp_setup () { +method smoke_nqp_setup() { return do_run(%*BIN, 'setup.nqp', 'smoke'); } @@ -482,10 +473,9 @@ method smoke_winxed_setup() { return do_run(_get_winxed(), 'setup.winxed', 'smoke'); } - # INSTALL -method install () { +method install() { my %inst := $!metadata.metadata; if %inst { say("\nInstalling $!name ..."); @@ -507,19 +497,19 @@ method install () { } } -method install_make () { +method install_make() { return self.do_with_privs(%*BIN, 'install'); } -method install_rake () { +method install_rake() { return self.do_with_privs(%*BIN, 'install'); } -method install_parrot_setup () { +method install_parrot_setup() { return self.do_with_privs(%*BIN, 'setup.pir', 'install'); } -method install_nqp_setup () { +method install_nqp_setup() { return self.do_with_privs(%*BIN, 'setup.nqp', 'install'); } @@ -527,10 +517,9 @@ method install_winxed_setup() { return self.do_with_privs(_get_winxed(), 'setup.winxed', 'install'); } - # UNINSTALL -method uninstall () { +method uninstall() { my %uninst := $!metadata.metadata; if %uninst { say("\nUninstalling $!name ..."); @@ -552,15 +541,15 @@ method uninstall () { } } -method uninstall_make () { +method uninstall_make() { return self.do_with_privs(%*BIN, 'uninstall'); } -method uninstall_parrot_setup () { +method uninstall_parrot_setup() { return self.do_with_privs(%*BIN, 'setup.pir', 'uninstall'); } -method uninstall_nqp_setup () { +method uninstall_nqp_setup() { return self.do_with_privs(%*BIN, 'setup.nqp', 'uninstall'); } @@ -568,7 +557,7 @@ method uninstall_winxed_setup() { return self.do_with_privs(_get_winxed(), 'setup.winxed', 'uninstall'); } -method do_with_privs (*@cmd) { +method do_with_privs(*@cmd) { my $bin_dir := %*VM; my $root_cmd := replace_config_strings(%*CONF); @@ -580,10 +569,9 @@ method do_with_privs (*@cmd) { } } - # CLEAN -method clean () { +method clean() { unless path_exists($!source_dir) { say("\nProject source dir '$!source_dir' does not exist; nothing to do."); return 1; @@ -603,19 +591,19 @@ method clean () { } } -method clean_make () { +method clean_make() { return do_run(%*BIN, 'clean'); } -method clean_rake () { +method clean_rake() { return do_run(%*BIN, 'clean'); } -method clean_parrot_setup () { +method clean_parrot_setup() { return do_run(%*BIN, 'setup.pir', 'clean'); } -method clean_nqp_setup () { +method clean_nqp_setup() { return do_run(%*BIN, 'setup.nqp', 'clean'); } @@ -623,10 +611,9 @@ method clean_winxed_setup() { return do_run(_get_winxed(), 'setup.winxed', 'clean'); } - # REALCLEAN -method realclean () { +method realclean() { unless path_exists($!source_dir) { say("\nProject source dir '$!source_dir' does not exist; nothing to do."); return 1; @@ -646,10 +633,12 @@ method realclean () { } } -method realclean_make () { +method realclean_make() { return do_run(%*BIN, 'realclean'); } -method realclean_rake () { +method realclean_rake() { return do_run(%*BIN, 'clobber'); } + +# vim: ft=perl6 diff --git a/src/lib/Plumage/Util.nqp b/src/lib/Plumage/Util.nqp index 380922e..e905bf1 100644 --- a/src/lib/Plumage/Util.nqp +++ b/src/lib/Plumage/Util.nqp @@ -1,3 +1,5 @@ +# Copyright (C) 2009-2011, Parrot Foundation. + =begin =head1 NAME @@ -12,7 +14,6 @@ Plumage::Util - Plumage-specific utility functions # Plumage-specific $replaced := replace_config_strings($original); - =head1 DESCRIPTION These utility functions are likely only directly useful to Plumage-related @@ -47,7 +48,7 @@ producing unintended expansions. =end -sub replace_config_strings ($original) { +sub replace_config_strings($original) { my $new := $original; repeat { @@ -59,7 +60,7 @@ sub replace_config_strings ($original) { return $new; } -sub config_value ($match) { +sub config_value($match) { my $key := $match; my $config := %*CONF{$key} || %*VM{$key} @@ -70,9 +71,10 @@ sub config_value ($match) { return $config; } - =begin =back =end + +# vim: ft=perl6 diff --git a/src/plumage.nqp b/src/plumage.nqp index 0a1c387..7fc0efa 100644 --- a/src/plumage.nqp +++ b/src/plumage.nqp @@ -1,9 +1,8 @@ -### -### NQP WORKAROUND HACKS -### +# Copyright (C) 2009-2011, Parrot Foundation. +# TODO Add perldoc -# Must declare all 'setting globals' here, because NQP doesn't know about them +# Global setting variables (NQP workaround) my $*PROGRAM_NAME; my $*OSNAME; my @*ARGS; @@ -11,134 +10,126 @@ my %*ENV; my %*VM; my $*OS; +load_libraries(); -# NQP does not include a setting, so must load helper libraries first -load_helper_libraries(); - - -# NQP does not have full {...} hash syntax, so use hash() and named args +# Global structure of recognized commands my %COMMANDS := hash( - usage => hash( - action => command_usage, - args => 'none', - usage => 'usage', - help => 'This command is here for compatibility purposes. Please use `help` instead.' - ), - help => hash( - action => command_help, - args => 'opt_command', - usage => 'help []', - help => 'Print a helpful usage message.' - ), - version => hash( - action => command_version, - args => 'none', - usage => 'version', - help => 'Print program version and copyright.', - ), - projects => hash( - action => command_projects, - args => 'none', - usage => 'projects', - help => 'List all known projects.' - ), - status => hash( - action => command_status, - args => 'opt_project', - usage => 'status []', - help => 'Show status of projects (defaults to all).' - ), - info => hash( - action => command_info, - args => 'project', - usage => 'info ', - help => 'Print summary about a particular project.' - ), - metadata => hash( - action => command_info, - args => 'project', - usage => 'metadata ', - help => 'Print JSON metadata about a particular project.' - ), - project_dir => hash( - action => command_project_dir, - args => 'project', - usage => 'project-dir ', - help => 'Print project\'s top directory' - ), - showdeps => hash( - action => command_showdeps, - args => 'project', - usage => 'showdeps ', - help => 'Show dependency resolution for a project.' - ), - fetch => hash( - action => command_project_action, - args => 'project', - usage => 'fetch ', - help => 'Download source.' - ), - update => hash( - action => command_project_action, - args => 'project', - usage => 'update ', - help => 'Update source (falls back to fetch).' - ), - configure => hash( - action => command_project_action, - args => 'project', - usage => 'configure ', - help => 'Configure source (updates first).' - ), - build => hash( - action => command_project_action, - args => 'project', - usage => 'build ', - help => 'Build project from source (configures first).' - ), - test => hash( - action => command_project_action, - args => 'project', - usage => 'test ', - help => 'Test built project (builds first).' - ), - smoke => hash( - action => command_project_action, - args => 'project', - usage => 'smoke ', - help => 'Smoke test project (builds first).' - ), - install => hash( - action => command_project_action, - args => 'project', - usage => 'install ', - help => 'Install built files (tests first).' - ), - uninstall => hash( - action => command_project_action, - args => 'project', - usage => 'uninstall ', - help => 'Uninstalls installed files (not always available).' - ), - clean => hash( - action => command_project_action, - args => 'project', - usage => 'clean ', - help => 'Clean source tree.' - ), - realclean => hash( - action => command_project_action, - args => 'project', - usage => 'realclean ', - help => 'Clobber/realclean source tree.' - ), -); - -# Work around NQP limitation with key names on the left of => -# (and as a side benefit, support both spellings) + usage => Plumage::Command.new(:action(command_usage), + :args('none'), + :usage('usage'), + :help('This command is here for compatibility ' + ~ 'only. Please use `help` instead.')), + + cli => Plumage::Command.new(:action(command_cli), + :args('none'), + :usage('cli'), + :help('Starts the interactive command-line interface. ' + ~ 'Invoked by default if no command was specified.')), + + help => Plumage::Command.new(:action(command_help), + :args('opt_command'), + :usage('help []'), + :help('Displays a help message on usage ' + ~ '(defaults to all).')), + + version => Plumage::Command.new(:action(command_version), + :args('none'), + :usage('version'), + :help('Displays Plumage version and copyright statement.')), + + projects => Plumage::Command.new(:action(command_projects), + :args('none'), + :usage('projects'), + :help('Lists all known projects.')), + + status => Plumage::Command.new(:action(command_status), + :args('opt_project'), + :usage('status '), + :help('Shows status of (defaults to all).')), + + info => Plumage::Command.new(:action(command_info), + :args('project'), + :usage('info '), + :help('Displays detailed description of .')), + + metadata => Plumage::Command.new(:action(command_info), + :args('project'), + :usage('metadata '), + :help('Displays JSON metadata for .')), + + project_dir => Plumage::Command.new(:action(command_project_dir), + :args('project'), + :usage('project-dir '), + :help('Displays top directory for .')), + + show_deps => Plumage::Command.new(:action(command_show_deps), + :args('project'), + :usage('show-deps '), + :help('Shows dependencies for .')), + + fetch => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('fetch '), + :help('Downloads source code for .')), + + update => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('update '), + :help('Updates source code for ' + ~ "(falls back to 'fetch').")), + + configure => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('configure '), + :help('Configures source code for ' + ~ "(runs 'update' first).")), + + build => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('build '), + :help('Builds in current directory ' + ~ "(runs 'configure' first).")), + + test => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('test '), + :help('Runs test suite for ' + ~ "(runs 'build' first).")), + + smoke => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('smoke '), + :help("Sends test results to Parrot's Smolder server " + ~ "(runs 'build' first).")), + + install => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('install '), + :help("Installs (runs 'test' first).")), + + uninstall => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('uninstall '), + :help('Uninstalls from system ' + ~ '(not always available).')), + + clean => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('clean '), + :help('Performs basic clean up of source tree ' + ~ 'for .')), + + realclean => Plumage::Command.new(:action(command_project_action), + :args('project'), + :usage('realclean '), + :help('Removes all files generated during the ' + ~ 'build process for .'))); + +# Add support for both spellings of certain commands %COMMANDS := %COMMANDS; +%COMMANDS := %COMMANDS; - +# Default configuration my %DEFAULT_CONF := hash( parrot_user_root => '#user_home_dir#/.parrot', plumage_user_root => '#parrot_user_root#/plumage', @@ -153,57 +144,66 @@ unless path_exists(%DEFAULT_CONF) { %DEFAULT_CONF := %*VM ~ '/plumage/metadata'; } - -# NQP does not automatically call MAIN() MAIN(); - ### -### INIT +### INITIALIZATION +### +### The following subroutines perform the various tasks that need to be +### performed before any commands are executed such as loading libraries, +### parsing command-line options, and reading the config file ### +# XXX Why is this package-scoped? Can it be declared with 'my'? +our %OPTIONS; # Command-line switches -our %OPT; - -my %*CONF; -my %*BIN; - +my %*CONF; # Configuration options +my %*BIN; # System binaries -sub load_helper_libraries () { - # Support OO +sub load_libraries() { + # Object-oriented interface pir::load_bytecode('P6object.pbc'); - # Process command line options + # Processes command-line switches pir::load_bytecode('Getopt/Obj.pbc'); - # Parse files in JSON format + # Parses JSON config files pir::load_bytecode('Config/JSON.pbc'); - # Data structure dumper for PMCs (used for debugging) + # "Stringifies" PMC data structures (used for debugging only) pir::load_bytecode('dumper.pbc'); - # Utility functions and standard "globals" + # Extends NQP runtime environment and native data structures pir::load_bytecode('Plumage/NQPUtil.pbc'); - # Plumage modules: util, metadata, project, dependencies + # Provides subroutines needed to replace config strings pir::load_bytecode('Plumage/Util.pbc'); + + # Parses a project's metadata pir::load_bytecode('Plumage/Metadata.pbc'); + + # Represents a project and performs certain actions on them pir::load_bytecode('Plumage/Project.pbc'); + + # Resolves dependencies pir::load_bytecode('Plumage/Dependencies.pbc'); + + # Represents Plumage commands + pir::load_bytecode('Plumage/Command.pbc'); } -sub parse_command_line_options () { - my $getopts := pir::root_new__PP(< parrot Getopt Obj >); +sub parse_command_line_options() { + my $getopt := pir::root_new__PP(< parrot Getopt Obj >); # Configure -c switch - my $config := $getopts.add(); + my $config := $getopt.add(); $config.name('CONFIG_FILE'); $config.long('config-file'); $config.short('c'); $config.type('String'); # Configure -i switch - my $ignore := $getopts.add(); + my $ignore := $getopt.add(); $ignore.name('IGNORE_FAIL'); $ignore.long('ignore-fail'); $ignore.short('i'); @@ -211,17 +211,16 @@ sub parse_command_line_options () { $ignore.optarg(1); # Configure -h switch - my $help := $getopts.add(); + my $help := $getopt.add(); $help.name('HELP'); $help.long('help'); $help.short('h'); - # Parse @*ARGS - %OPT := $getopts.get_options(@*ARGS); + %OPTIONS := $getopt.get_options(@*ARGS); } -sub read_config_files () { - # Find config files for this system and user (ignored if missing). +sub read_config_files() { + # Find config file for this system and user (if any) my $etc := %*VM; my $home := %*ENV || user_home_dir(); my $base := 'plumage.json'; @@ -229,12 +228,12 @@ sub read_config_files () { my $userconf := fscat([$home, 'parrot', 'plumage'], $base); my @configs := ($sysconf, $userconf); - # Remember home dir, we'll need that later + # Remember home directory %*CONF := $home; - # If another config specified via command line option, add it. Because - # this was manually set by the user, it is a fatal error if missing. - my $optconf := %OPT; + # Use config file given on command-line, if given + my $optconf := %OPTIONS; + if $optconf { if path_exists($optconf) { @configs.push($optconf); @@ -244,7 +243,7 @@ sub read_config_files () { } } - # Merge together default, system, user, and option configs + # Merge together 'default', 'system', 'user', and 'option' config options %*CONF := merge_tree_structures(%*CONF, %DEFAULT_CONF); for @configs -> $config { @@ -253,20 +252,20 @@ sub read_config_files () { %*CONF := merge_tree_structures(%*CONF, %conf); CATCH { - say("Could not parse JSON file '$config'."); + say("Could not parse config file '$config'."); } } } } -sub merge_tree_structures ($dst, $src) { +sub merge_tree_structures($dst, $src) { for $src.keys -> $k { my $d := $dst{$k}; my $s := $src{$k}; - if $d && pir::does__IPs($d, 'hash') - && $s && pir::does__IPs($s, 'hash') { - $dst{$k} := merge_tree_structures($d, $s); + if $d && pir::does__IPs($d, 'hash') + && $s && pir::does__IPs($s, 'hash') { + $dst{$k} := merge_tree_structures($d, $s); } else { $dst{$k} := $s; @@ -276,133 +275,108 @@ sub merge_tree_structures ($dst, $src) { return $dst; } -sub find_binaries () { +sub find_binaries() { my %conf := %*VM; my $parrot_bin := %conf; - # Parrot programs; must be sourced from configured parrot bin directory + # Parrot binaries (must be sourced from configured Parrot bin directory) %*BIN := fscat([$parrot_bin], 'parrot_config'); %*BIN := fscat([$parrot_bin], 'parrot-nqp'); %*BIN := fscat([$parrot_bin], 'parrot'); - # Programs used to build parrot; make sure we use the same ones - %*BIN := %conf; - %*BIN := %conf; - - # Unrelated system programs; look for them in the user's search path - %*BIN := find_program('rake'); - %*BIN := find_program('svn'); - %*BIN := find_program('git'); - %*BIN := find_program('hg'); -} - + # Use the same programs used to build Parrot + %*BIN := %conf; + %*BIN := %conf; -### -### MAIN -### - - -sub MAIN () { - parse_command_line_options(); - read_config_files(); - find_binaries(); - - if %OPT.exists('HELP') { - execute_command('help'); - } - else { - my $command := parse_command_line(); - execute_command($command); - } + # Additional programs needed to fetch project's source code + %*BIN := find_program('rake'); + %*BIN := find_program('svn'); + %*BIN := find_program('git'); + %*BIN := find_program('hg'); } -sub parse_command_line () { - my $command := @*ARGS ?? @*ARGS.shift !! 'help'; - - return $command; +sub parse_command_line() { + return @*ARGS ?? @*ARGS.shift !! 'cli'; } -sub execute_command ($command) { - my $action := %COMMANDS{$command}; - my $args := %COMMANDS{$command}; +sub execute_command($command) { + my $action := %COMMANDS{$command}.action; + my $args := %COMMANDS{$command}.args; - if ($action) { + if $action { if $args eq 'project' && !@*ARGS { say('Please specify a project to act on.'); } - #elsif $args eq 'opt_project' { - #} else { $action(@*ARGS, :command($command)); } } else { - say("I don't know how to '$command'!"); - pir::exit(1); + say("No such command: $command. Please use $*PROGRAM_NAME --help"); + pir::exit__vi(1); } } - ### ### COMMANDS ### +### Each of the following subroutines represents an individual command recognized +### by Plumage (note the command_* prefix followed by the actual command name) +### - -sub command_usage () { +sub command_usage() { print(usage_info()); } - -sub usage_info () { +sub usage_info() { return "Usage: $*PROGRAM_NAME [] [] Options: - -h, --help Print a helpful usage message + -h, --help Displays a help message on command usage. - -c, --config-file= Read additional config file + -c, --config-file= Reads additional config file in . - -i, --ignore-fail Ignore any failing build stages + -i, --ignore-fail Ignores any failed build stages. - -i, --ignore-fail= Ignore failures only in a particular stage - (may be repeated to select more than one stage) + -i, --ignore-fail= Ignores failures only for + (may be repeated to select more than one stage). - -i, --ignore-fail==0 Don't ignore failures in this stage + -i, --ignore-fail==0 Doesn't ignore failures in . Commands: - Query metadata/project info: - projects List all known projects - status [] Show status of projects (defaults to all) - info Print summary about a particular project - metadata Print JSON metadata about a particular project - showdeps Show dependency resolution for a project - project-dir Print project's top directory + Query metadata and project info: + projects Lists all known projects. + status [] Shows status of (defaults to all). + info Displays detailed description of . + metadata Displays JSON metadata for . + show-deps Shows dependencies for . + project-dir Displays top directory for . Perform actions on a project: - fetch Download source - update Update source (falls back to fetch) - configure Configure source (updates first) - build Build project from source (configures first) - test Test built project (builds first) - smoke Smoke test project (builds first) - install Install built files (tests first) - uninstall Uninstalls installed files (not always available) - clean Clean source tree - realclean Clobber/realclean source tree - - Get info about Plumage itself: - version Print program version and copyright - help [] Print a helpful usage message + fetch Downloads source code for . + update Updates source code for (falls back to fetch). + configure Configures source code for (runs 'update' first). + build Builds in current directory (runs 'configure' first). + test Runs test suite for (runs 'build' first). + smoke Sends test results to Parrot's Smolder server (runs 'build' first). + install Installs (runs 'test' first). + uninstall Uninstalls from system (not always available). + clean Performs basic cleanup of source tree for . + realclean Removes all generated files during the build process for . + + Get information about Plumage: + version Displays Plumage version and copyright statement. + help [] Displays a help message on usage (defaults to all). "; } - -sub command_help ($help_cmd, :$command) { +sub command_help($help_cmd, :$command) { if ?$help_cmd { - my $usage := %COMMANDS{$help_cmd[0]}; - my $help := %COMMANDS{$help_cmd[0]}; + my $usage := %COMMANDS{$help_cmd[0]}.usage; + my $help := %COMMANDS{$help_cmd[0]}.help; # Check that command actually exists if ($usage eq '') || ($help eq '') { @@ -417,26 +391,58 @@ sub command_help ($help_cmd, :$command) { } } +sub command_cli() { + pir::load_bytecode('Plumage/Interactive.pbc'); -sub command_version () { + my $session := Plumage::Interactive.new(:prompt_string('plumage')); + my $input; + + # Main runloop + while 1 { + $session.prompt(); + + pir::exit__vi(0) if $session.input eq 'quit'; + + # @command[0] contains the given command + # @command[1] contains the arguments + my @command := $session.parse_command_line(); + + my $action := %COMMANDS{@command[0]}.action; + my $args := %COMMANDS{@command[0]}.args; + + if $action { + if $args eq 'project' && !@command[1] { + say('Please specify a project to act on.'); + } + else { + $action([@command[1]], :command(@command[0])); + } + } + else { + say("No such command: $input. Trying using 'help'."); + pir::exit__vi(1); + } + } +} + +sub command_version() { print(version_info()); } -sub version_info () { +sub version_info() { my $version := '0'; return -"This is Parrot Plumage, version $version. +"This is Plumage, version $version. -Copyright (C) 2009, Parrot Foundation. +Copyright (C) 2009-2011, Parrot Foundation. This code is distributed under the terms of the Artistic License 2.0. For more details, see the full text of the license in the LICENSE file -included in the Parrot Plumage source tree. +included in the Plumage source tree. "; } - -sub command_projects () { +sub command_projects() { my @projects := Plumage::Metadata.get_project_list(); @projects.sort; @@ -465,13 +471,12 @@ sub command_projects () { say(''); } - -sub command_status (@projects, :$command) { +sub command_status(@projects, :$command) { my $showing_all := !@projects; unless @projects { @projects := Plumage::Metadata.get_project_list(); - say("\nKnown projects:\n"); + say("Known projects:\n"); } my @installed := Plumage::Dependencies.get_installed_projects(); @@ -486,11 +491,8 @@ sub command_status (@projects, :$command) { say('') if $showing_all; } - -sub command_info (@projects, :$command) { - unless (@projects) { - say('Please include the name of the project you wish info for.'); - } +sub command_info(@projects, :$command) { + say('You must specify the name of the project.') unless @projects; for @projects -> $project { my $meta := Plumage::Metadata.new(); @@ -510,8 +512,7 @@ sub command_info (@projects, :$command) { } } - -sub command_showdeps (@projects, :$command) { +sub command_show_deps(@projects, :$command) { unless (@projects) { say('Please include the name of the project to show dependencies for.'); } @@ -532,12 +533,11 @@ sub command_showdeps (@projects, :$command) { } } -sub report_metadata_error ($project_name, $meta) { +sub report_metadata_error($project_name, $meta) { say("Metadata error for project '$project_name':\n" ~ $meta.error); } - -sub command_project_dir (@projects, :$command) { +sub command_project_dir(@projects, :$command) { unless (@projects) { say('Please include the name of the project you wish to find.'); } @@ -549,14 +549,12 @@ sub command_project_dir (@projects, :$command) { } } - -sub command_project_action (@projects, :$command) { +sub command_project_action(@projects, :$command) { install_required_projects(@projects) && perform_actions_on_projects(@projects, :up_to($command)); } - -sub install_required_projects (@projects) { +sub install_required_projects(@projects) { my %resolutions := Plumage::Dependencies.resolve_dependencies(@projects); my @need_projects := %resolutions; @@ -571,7 +569,7 @@ sub install_required_projects (@projects) { return 1; } -sub show_dependencies (@projects) { +sub show_dependencies(@projects) { my %resolutions := Plumage::Dependencies.resolve_dependencies(@projects); say(''); @@ -592,7 +590,7 @@ sub show_dependencies (@projects) { say("Missing and unrecognized: $need_unknown"); if $need_unknown { - # XXXX: Don't forget to fix this when metadata is retrieved from server + # XXX Don't forget to fix this when metadata is retrieved from server say("\nI don't recognize some of these dependencies. First, update and\n" ~ "rebuild Plumage to get the latest metadata. Next, please check\n" @@ -614,14 +612,14 @@ sub show_dependencies (@projects) { } } - -sub perform_actions_on_projects (@projects, :$up_to, :@actions) { - my $has_ignore_flag := %OPT.exists('IGNORE_FAIL'); - my %ignore := %OPT; +sub perform_actions_on_projects(@projects, :$up_to, :@actions) { + my $has_ignore_flag := %OPTIONS.exists('IGNORE_FAIL'); + my %ignore := %OPTIONS; my $ignore_all := $has_ignore_flag && !%ignore; for @projects -> $project_name { my $project := Plumage::Project.new($project_name); + if pir::defined__IP($project) { return 0 unless $project.perform_actions(:up_to($up_to), :actions(@actions), @@ -633,8 +631,7 @@ sub perform_actions_on_projects (@projects, :$up_to, :@actions) { return 1; } - -sub print_project_summary ($meta) { +sub print_project_summary($meta) { my %general := $meta; my $name := %general; @@ -653,3 +650,19 @@ sub print_project_summary ($meta) { say(pir::sprintf__SsP("%-11s : %s", ["License", $license])); say(pir::sprintf__SsP("%-11s : %s", ["Description", $description])); } + +sub MAIN() { + parse_command_line_options(); + read_config_files(); + find_binaries(); + + if %OPTIONS.exists('HELP') { + execute_command('help'); + } + else { + my $command := parse_command_line(); + execute_command($command); + } +} + +# vim: ft=perl6 diff --git a/t/01-sanity.t b/t/01-sanity.t index b71a90b..288f987 100755 --- a/t/01-sanity.t +++ b/t/01-sanity.t @@ -2,7 +2,7 @@ MAIN(); -sub MAIN () { +sub MAIN() { # Load testing tools pir::load_language('parrot'); pir::compreg__PS('parrot').import('Test::More'); @@ -11,17 +11,21 @@ sub MAIN () { run_tests(); } -sub run_tests () { +sub run_tests() { plan(6); test_testing(); } sub test_testing() { - ok( 1, 'ok works'); - nok( 0, 'nok works'); - is( 5, 5, 'is works for ints'); - is( 'z', 'z', 'is works for strings'); - isnt( 8, -8, 'isnt works for ints'); - isnt('q', 'rs', 'isnt works for strings'); + ok(1, 'ok() works'); + nok(0, 'nok() works'); + + is(5, 5, 'is() works for ints'); + is('z', 'z', 'is() works for strings'); + + isnt(8, -8, 'isnt() works for ints'); + isnt('q', 'rs', 'isnt() works for strings'); } + +# vim: ft=perl6 diff --git a/t/02-load-all.t b/t/02-load-all.t index cf6f4d9..0c22aaf 100755 --- a/t/02-load-all.t +++ b/t/02-load-all.t @@ -2,7 +2,7 @@ MAIN(); -sub MAIN () { +sub MAIN() { # Load testing tools pir::load_language('parrot'); pir::compreg__PS('parrot').import('Test::More'); @@ -11,33 +11,34 @@ sub MAIN () { run_tests(); } -sub run_tests () { +sub run_tests() { plan(11); test_load_pbcs(); } sub test_load_pbcs() { - my @pbcs := < - config.pbc - dumper.pbc - Config/JSON.pbc - Getopt/Obj.pbc - P6object.pbc - P6Regex.pbc - Plumage/NQPUtil.pbc - Plumage/Util.pbc - Plumage/Metadata.pbc - Plumage/Dependencies.pbc - Plumage/Project.pbc - >; + my @pbcs := ; for @pbcs -> $pbc { pir::load_bytecode($pbc); ok(1, "success loading '$pbc'"); + CATCH { ok(0, "FAILED TO LOAD '$pbc'"); } } } + +# vim: ft=perl6 diff --git a/t/03-plumage-nqputil.t b/t/03-plumage-nqputil.t index 60202ba..5087ef1 100755 --- a/t/03-plumage-nqputil.t +++ b/t/03-plumage-nqputil.t @@ -4,7 +4,7 @@ my $*EXECUTABLE_NAME; MAIN(); -sub MAIN () { +sub MAIN() { # Load testing tools pir::load_language('parrot'); pir::compreg__PS('parrot').import('Test::More'); @@ -16,7 +16,7 @@ sub MAIN () { run_tests(); } -sub run_tests () { +sub run_tests() { plan(51); test_hash_exists(); @@ -40,17 +40,18 @@ sub run_tests () { sub test_all_matches() { my @matches; @matches := all_matches(/ab?d?x?c/,"abc y adcef x axcfoo twiddle"); - is(@matches[0],'abc','all_matches found abc'); - is(@matches[1],'adc','all_matches found adc'); - is(@matches[2],'axc','all_matches found axc'); + + is(@matches[0], 'abc', 'all_matches found abc'); + is(@matches[1], 'adc', 'all_matches found adc'); + is(@matches[2], 'axc', 'all_matches found axc'); } sub test_hash() { - my %hash := hash( monkey => 'see'); + my %hash := hash(monkey => 'see'); my @kv := %hash.kv; - is(@kv[0],'monkey', 'has() creates the monkey key'); - is(@kv[1],'see', 'hash() set the value of monkey correctly'); + is(@kv[0], 'monkey', 'hash() creates the monkey key'); + is(@kv[1], 'see', 'hash() set the value of monkey correctly'); } @@ -58,7 +59,7 @@ sub test_hash_exists() { my %opt; %opt := 42; - ok( %opt.exists('foobar'), 'exists works for existing keys'); + ok(%opt.exists('foobar'), 'exists works for existing keys'); nok(%opt.exists('zanzibar'), 'exists works for non-existent keys'); } @@ -67,14 +68,16 @@ sub test_hash_values() { my @values := %hash.values; is(@values, 0, 'values on empty hash is empty'); - %hash := 42; + %hash := 42; @values := %hash.values; - is(@values, 1, 'values on hash with one entry has one element'); + + is(@values, 1, 'values on hash with one entry has one element'); is(@values[0], 42, '... and that element is correct'); %hash := 99; - is( %hash.values, 2, 'values on hash with two entries has two elements'); + + is(%hash.values, 2, 'values on hash with two entries has two elements'); } sub test_hash_keys() { @@ -149,26 +152,31 @@ sub test_set_from_array() { my @array; my %set := set_from_array(@array); my @keys := %set.keys; + is(@keys, 0, 'set_from_array on empty array produces empty set'); @array := (1, "two", "two", 3, '3', 3); %set := set_from_array(@array); @keys := %set.keys; + is(@keys, 3, 'set_from_array on array with dups has correct number of keys'); is(%set<1>, 1, '... and first key is in set'); is(%set, 1, '... and second key is in set'); is(%set<3>, 1, '... and third key is in set'); + nok(%set.exists('four'), '... and non-existant key is not in set'); } sub test_subst() { my $string := 'chewbacca'; my $subst := subst($string, /a/, 'x'); + is($subst, 'chewbxccx', 'subst works with plain string replacement'); is($string, 'chewbacca', 'plain string subst edits a clone'); my $text := 'wookie'; my $fixed := subst($text, /w|k/, replacement); + is($fixed, 'wwookkie', 'subst works with code replacement'); is($text, 'wookie', 'code replacement subst edits a clone'); } @@ -180,12 +188,12 @@ sub replacement($match) { } sub test_path_exists() { - ok( path_exists('.'), 'path_exists finds .'); + ok(path_exists('.'), 'path_exists finds .'); nok(path_exists('DOESNOTEXIST'), 'path_exists returns false for nonexistent files'); } sub test_is_dir() { - ok( is_dir('.'), '. is a directory'); + ok(is_dir('.'), '. is a directory'); nok(is_dir('DOESNOTEXIST'), 'is_dir returns false for nonexistent dirs'); nok(is_dir('harness'), 'is_dir returns false for normal files'); } @@ -197,14 +205,19 @@ sub test_qx() { is(qx(''), '', 'qx("") returns an empty string'); $output := qx('IHOPETHATTHISPATHDOESNOTEXISTANDISEXECUTABLEANDRETURNSTRUE'); + ok($output ~~ /:s not [found|recognized]/, 'qx() on invalid path returns not found error'); isnt($!, 0, '... and the exit status is non-zero'); $output := qx($*EXECUTABLE_NAME, '-e', '"say(42); pir::exit(0)"'); + is($output, "42\n", 'qx() captures output of exit(0) program, retaining line endings'); is($!, 0, '... and the exit status is correct'); $output := qx($*EXECUTABLE_NAME, '-e', '"say(21); pir::exit(1)"'); + is($output, "21\n", 'qx() captures output of exit(1) program, retaining line endings'); is($!, 1, '... and the exit status is correct'); } + +# vim: ft=perl6 diff --git a/t/sanity.t b/t/sanity.t index 6faf676..be2350e 100755 --- a/t/sanity.t +++ b/t/sanity.t @@ -5,7 +5,7 @@ my $!; MAIN(); -sub MAIN () { +sub MAIN() { # Load testing tools pir::load_language('parrot'); pir::compreg__PS('parrot').import('Test::More'); @@ -20,8 +20,8 @@ sub MAIN () { run_tests(); } -sub run_tests () { - plan(22); +sub run_tests() { + plan(20); # Fuzz tests test_invalid(); @@ -33,7 +33,6 @@ sub run_tests () { test_plumage_install_invalid(); # Missing argument tests - test_plumage_no_args(); test_plumage_fetch_no_args(); # Behavior tests @@ -44,68 +43,67 @@ sub run_tests () { test_plumage_metadata(); } - # # FUZZ TESTS # sub test_invalid() { qx('invalidjunkdoesnotexist'); + nok($! == 0, 'do_run()ing invalidjunk returns false'); } sub test_plumage_invalid() { qx($PLUMAGE, 'asdfversion'); + nok($! == 0, 'plumage returns failure for invalid commands'); } sub test_plumage_info_invalid() { my $output := qx($PLUMAGE, 'info', 'coboloncogs'); + ok($output ~~ /:s I don.t know anything about project .coboloncogs./, - "command 'info' errors properly for unknown project name"); + "command 'info' errors properly for unknown project name"); } sub test_plumage_configure_invalid() { my $output := qx($PLUMAGE, 'configure', 'coboloncogs'); + ok($output ~~ /:s I don.t know anything about project .coboloncogs./, - "command 'configure' errors properly for unknown project name"); + "command 'configure' errors properly for unknown project name"); } sub test_plumage_build_invalid() { my $output := qx($PLUMAGE, 'build', 'coboloncogs'); + ok($output ~~ /:s I don.t know anything about project .coboloncogs./, - "command 'build' errors properly for unknown project name"); + "command 'build' errors properly for unknown project name"); } sub test_plumage_test_invalid() { my $output := qx($PLUMAGE, 'test', 'coboloncogs'); + ok($output ~~ /:s I don.t know anything about project .coboloncogs./, - "command 'test' errors properly for unknown project name"); + "command 'test' errors properly for unknown project name"); } sub test_plumage_install_invalid() { my $output := qx($PLUMAGE, 'install', 'coboloncogs'); + ok($output ~~ /:s I don.t know anything about project .coboloncogs./, - "command 'install' errors properly for unknown project name"); + "command 'install' errors properly for unknown project name"); } - # # MISSING ARGUMENT TESTS # -sub test_plumage_no_args() { - my $output := qx($PLUMAGE); - ok($output ~~ /:s Print program version and copyright/, 'no args give usage'); - ok($output ~~ /:s Print summary about a particular project/, 'no args give usage'); -} - sub test_plumage_fetch_no_args() { my $output := qx($PLUMAGE, 'fetch'); + ok($output ~~ /:s Please specify a project to act on./, 'fetch without args asks for project name'); } - # # BEHAVIOR TESTS # @@ -122,33 +120,38 @@ sub test_plumage_usage() { sub test_plumage_help() { my $output := qx($PLUMAGE, 'help'); - ok($output ~~ /:s Print program version and copyright/, + ok($output ~~ /:s Displays a help message on \ usage \(defaults to all\)\./, 'help explains how to view version and copyright'); - ok($output ~~ /:s Print summary about a particular project/, + ok($output ~~ /:s Displays detailed description of \\./, 'help explains how to get info on a project'); $output := qx($PLUMAGE, 'help', 'info'); - ok($output ~~ /:s info \/, 'help info displays usage'); - ok($output ~~ /:s Print summary about a particular project\./, 'help info displays help'); + ok($output ~~ /:s info \/, 'help info displays usage'); + ok($output ~~ /:s Displays detailed description of \\./, 'help info displays help'); } sub test_plumage_version() { my $output := qx($PLUMAGE, 'version'); - ok($! == 0, 'plumage version returns success'); - ok($output ~~ /:s Parrot Plumage/, 'plumage version knows its name'); + + ok($! == 0, 'plumage version returns success'); + ok($output ~~ /:s Plumage/, 'plumage version knows its name'); ok($output ~~ /:s Parrot Foundation/, 'version mentions Parrot Foundation'); ok($output ~~ /:s Artistic License/, 'version mentions Artistic License'); } sub test_plumage_info() { my $output := qx($PLUMAGE, 'info', 'rakudo'); + ok($output ~~ /:s Perl 6 on Parrot/, 'info rakudo'); ok($output ~~ /:s Name\s+\:\s+Rakudo/, 'info rakudo'); } sub test_plumage_metadata() { my $output := qx($PLUMAGE, 'metadata', 'nqp-rx'); + ok($output ~~ /:s Not Quite Perl 6/, 'metadata nqp-rx'); ok($output ~~ /dependency\-info/, 'metadata nqp-rx'); } + +# vim: ft=perl6