Файловый менеджер - Редактировать - /home/jogoso94/public_html/static/img/logo/Capture-Tiny-0.48-0.tar
�азад
xt/author/pod-coverage.t 0000644 00000000334 15043642076 0011250 0 ustar 00 #!perl # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); xt/author/pod-syntax.t 0000644 00000000252 15043642076 0011002 0 ustar 00 #!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); xt/author/test-version.t 0000644 00000000637 15043642076 0011345 0 ustar 00 use strict; use warnings; use Test::More; # generated by Dist::Zilla::Plugin::Test::Version 1.09 use Test::Version; my @imports = qw( version_all_ok ); my $params = { is_strict => 0, has_version => 1, multiple => 0, }; push @imports, $params if version->parse( $Test::Version::VERSION ) >= version->parse('1.002'); Test::Version->import(@imports); version_all_ok; done_testing; xt/author/critic.t 0000644 00000000201 15043642076 0010143 0 ustar 00 #!perl use strict; use warnings; use Test::Perl::Critic (-profile => "perlcritic.rc") x!! -e "perlcritic.rc"; all_critic_ok(); xt/author/00-compile.t 0000644 00000002661 15043642076 0010547 0 ustar 00 use 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.058 use Test::More; plan tests => 2; my @module_files = ( 'Capture/Tiny.pm' ); # fake home for cpan-testers use File::Temp; local $ENV{HOME} = File::Temp::tempdir( CLEANUP => 1 ); my @switches = ( -d 'blib' ? '-Mblib' : '-Ilib', ); use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L<perlfaq8/How can I capture STDERR from an external command?> my $stderr = IO::Handle->new; diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } $^X, @switches, '-e', "require q[$lib]")) if $ENV{PERL_COMPILE_TEST_DEBUG}; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { +require blib; blib->VERSION('1.01') }; if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ); xt/author/portability.t 0000644 00000000322 15043642076 0011234 0 ustar 00 use strict; use warnings; use Test::More; eval 'use Test::Portability::Files'; plan skip_all => 'Test::Portability::Files required for testing portability' if $@; options(test_one_dot => 0); run_tests(); xt/author/pod-spell.t 0000644 00000000640 15043642076 0010574 0 ustar 00 use strict; use warnings; use Test::More; # generated by Dist::Zilla::Plugin::Test::PodSpelling 2.007005 use Test::Spelling 0.12; use Pod::Wordlist; add_stopwords(<DATA>); all_pod_files_spelling_ok( qw( bin lib ) ); __DATA__ Capture Dagfinn David Golden Graham Ilmari Knop Mannsåker Peter Rabbitson Tiny UTF Wheeler dagolden david fecundf haarg ilmari lib not prototyped resending ribasushi seekable undiagnosed xt/author/minimum-version.t 0000644 00000000130 15043642076 0012025 0 ustar 00 #!perl use Test::More; use Test::MinimumVersion; all_minimum_version_ok( qq{5.010} ); xt/release/distmeta.t 0000644 00000000172 15043642076 0010625 0 ustar 00 #!perl # This file was automatically generated by Dist::Zilla::Plugin::MetaTests. use Test::CPAN::Meta; meta_yaml_ok(); CONTRIBUTING.mkdn 0000644 00000005751 15043642076 0007345 0 ustar 00 ## HOW TO CONTRIBUTE Thank you for considering contributing to this distribution. This file contains instructions that will help you work with the source code. The distribution is managed with Dist::Zilla. This means than many of the usual files you might expect are not in the repository, but are generated at release time, as is much of the documentation. Some generated files are kept in the repository as a convenience (e.g. Makefile.PL or cpanfile). Generally, **you do not need Dist::Zilla to contribute patches**. You do need Dist::Zilla to create a tarball. See below for guidance. ### Getting dependencies If you have App::cpanminus 1.6 or later installed, you can use `cpanm` to satisfy dependencies like this: $ cpanm --installdeps . Otherwise, look for either a `Makefile.PL` or `cpanfile` file for a list of dependencies to satisfy. ### Running tests You can run tests directly using the `prove` tool: $ prove -l $ prove -lv t/some_test_file.t For most of my distributions, `prove` is entirely sufficient for you to test any patches you have. I use `prove` for 99% of my testing during development. ### Code style and tidying Please try to match any existing coding style. If there is a `.perltidyrc` file, please install Perl::Tidy and use perltidy before submitting patches. If there is a `tidyall.ini` file, you can also install Code::TidyAll and run `tidyall` on a file or `tidyall -a` to tidy all files. ### Patching documentation Much of the documentation Pod is generated at release time. Some is generated boilerplate; other documentation is built from pseudo-POD directives in the source like C<=method> or C<=func>. If you would like to submit a documentation edit, please limit yourself to the documentation you see. If you see typos or documentation issues in the generated docs, please email or open a bug ticket instead of patching. ### Installing and using Dist::Zilla Dist::Zilla is a very powerful authoring tool, optimized for maintaining a large number of distributions with a high degree of automation, but it has a large dependency chain, a bit of a learning curve and requires a number of author-specific plugins. To install it from CPAN, I recommend one of the following approaches for the quickest installation: # using CPAN.pm, but bypassing non-functional pod tests $ cpan TAP::Harness::Restricted $ PERL_MM_USE_DEFAULT=1 HARNESS_CLASS=TAP::Harness::Restricted cpan Dist::Zilla # using cpanm, bypassing *all* tests $ cpanm -n Dist::Zilla In either case, it's probably going to take about 10 minutes. Go for a walk, go get a cup of your favorite beverage, take a bathroom break, or whatever. When you get back, Dist::Zilla should be ready for you. Then you need to install any plugins specific to this distribution: $ cpan `dzil authordeps` $ dzil authordeps | cpanm Once installed, here are some dzil commands you might try: $ dzil build $ dzil test $ dzil xtest You can learn more about Dist::Zilla at http://dzil.org/ pm_to_blib 0000644 00000000000 15043642076 0006572 0 ustar 00 Changes 0000644 00000023466 15043642076 0006061 0 ustar 00 Revision history for Capture-Tiny 0.48 2018-04-22 09:01:08+02:00 Europe/Oslo - No changes from 0.47-TRIAL 0.47 2017-07-26 10:34:24-04:00 America/New_York (TRIAL RELEASE) [Fixed] - Appends PID to random file names for tee signalling to avoid random name collision when used in multiple forked children. 0.46 2017-02-25 14:19:22-05:00 America/New_York - No changes from 0.45-TRIAL 0.45 2017-02-23 13:22:43-05:00 America/New_York (TRIAL RELEASE) [Internal] - Avoid variable shadowing to improve debuggability. 0.44 2016-08-05 13:40:33-04:00 America/New_York [Docs] - Note that dropping privileges during a capture can lead to temporary files not cleaned up. 0.42 2016-05-31 12:40:10-04:00 America/New_York - No changes from 0.41 0.41 2016-05-23 11:58:15-04:00 America/New_York (TRIAL RELEASE) [Fixed] - Fixed some failing tests when STDIN is routed to /dev/null 0.40 2016-05-23 11:42:35-04:00 America/New_York - No changes from 0.39 0.39 2016-05-02 10:21:48-04:00 America/New_York (TRIAL RELEASE) [Fixed] - Fix in 0.37 tickled a very obscure regular expressions bug in perl < 5.18; should now be fixed. 0.37 2016-05-02 07:08:31-04:00 America/New_York (TRIAL RELEASE) [Fixed] - Skip some tests if locale can't be determined. 0.36 2016-02-28 21:36:57-05:00 America/New_York [Docs] - Fixed typos. 0.34 2016-02-18 23:26:13-05:00 America/New_York [Fixed] - Removed spurious JSON::PP dependency added by a broken Dist::Zilla plugin. 0.32 2016-02-18 10:12:02-05:00 America/New_York [Docs] - Changed internal formatting of documentation [Changes] - No functional changes from 0.31 0.31 2016-02-14 07:33:50-07:00 America/Mazatlan (TRIAL RELEASE) [Fixed] - Application of layers to handles during and after capture now attempts to more accurately duplicate the original layers, including potential duplicate layers. Because of the unusual ways that layers are ordered and applied, exact duplication is not guaranteeed, but this should be better that what Capture::Tiny did before. - Avoids a hard crash on Windows with Perl < 5.20 if a fork occurs in a capture block. Also documented the risks and lack of support for forks in capture blocks. 0.30 2015-05-15 20:43:54-04:00 America/New_York No changes from 0.29 0.29 2015-04-19 18:36:24+02:00 Europe/Berlin (TRIAL RELEASE) Fixed: - Fix double filehandle close error with tee on Windows (which started warning during the perl 5.21.x series, causing tests to fail) 0.28 2015-02-11 06:39:51-05:00 America/New_York Tests: - Removes test that optionally uses Inline::C to avoid spurious test failures. Also Inline::C had become a fairly heavy (if optional) dependency. Docs: - Clarify that PERL_CAPTURE_TINY_TIMEOUT is an internal control, not a timeout of the code reference being captured. 0.27 2014-11-04 23:10:44-05:00 America/New_York Prereqs: - Make Inline::C recommended, not required 0.26 2014-11-04 06:55:15-05:00 America/New_York Tests: - Actually check for Inline::C in tests, not just Inline 0.25 2014-08-16 10:08:42-04:00 America/New_York Prereqs: - Amended recommended modules to list Inline::C rather than Inline 0.24 2014-02-06 17:15:37-05:00 America/New_York Fixed: - Closed security hole in use of semaphore file in /tmp; now opens the semaphore file using O_CREAT|O_EXCL 0.23 2013-10-20 11:25:34 America/New_York Fixed: - minimum Perl prereq is back to 5.6 (but $diety help you if you're still stuck on 5.6) Documented: - Added warning about using @_ in a capture block 0.22 2013-03-27 15:50:29 America/New_York Documented: - Issue tracker is now github 0.21 2012-11-14 19:04:49 America/New_York Changed: - Skips tee and leak tests for closed STDIN on Perl prior to 5.12 when PERL_UNICODE=D. Documented lack of support as a known issue. - Isolated tee subprocesses from effects of PERL_UNICODE as a precaution (though this did not fix the above issue). - Improved layer detection for handles proxied due to being closed or tied. 0.20 2012-09-19 13:20:57 America/New_York Fixed: - Nested merged captures that include an external program call no longer leak STDERR to the outer scope [rt.cpan.org #79376] 0.19 2012-08-06 20:26:34 America/New_York Fixed: - Work around rt.perl.org #114404 by forcing PerlIO layers back on original handles [rt.cpan.org #78819] 0.18 2012-05-04 16:31:53 America/New_York Added: - When capture or tee are called in void context, Capture::Tiny skips reading back from the capture handles if it can do so safely 0.17_52 2012-03-09 11:45:19 EST5EDT Fixed: - Tied STDIN is always localized before redirections to avoid tees hanging on MSWin32 - Copying and reopening STDIN is necessary to avoid tees hanging on MSWin32. 0.17_51 2012-03-07 18:22:34 EST5EDT Fixed: - Avoids reopening STDIN while setting up a capture, which avoids some problems with pathological tied filehandle implementations such as in FCGI Tested: - Re-enabled tied STDIN testing for MSWin32 to see if changes above avoid crashes seen historically 0.17 2012-02-22 08:07:41 EST5EDT Fixed: - Added a workaround for failing t/08-stdin-closed.t under blead perl / 5.15.8 [rt.perl.org #111070] Documented: - Clarified some limitations; added a link to CPAN Testers Matrix; removed redundant BUGS section; standardized terminology Tested: - Added a test using Inline::C to print to stdout and stderr in response to rt.cpan.org #71701 0.16 2012-02-12 21:04:24 EST5EDT Documented: - Noted problems and workaround for FCGI's pathological tied STDIN [rt.cpan.org #74681; thank you Karl Gaissmaier for testing the workaround] 0.15 2011-12-23 11:10:47 EST5EDT Fixed: - Repeated captures from a custom filehandle would return undef instead of the empty string (and would warn). This has been fixed. [rt.cpan.org #73374 part two. Thank you to Philipp Herz for help in reproducing this bug.] Other: - Commented out debugging code for slightly less runtime overhead 0.14 2011-12-22 10:14:09 EST5EDT Added: - Capturing with custom filehandles will return only newly appended output instead of everything already in the file. [rt.cpan.org #73374] 0.13 2011-12-02 13:39:00 EST5EDT Fixed: - Fixed t/18-custom-capture.t failures on Windows due to tempfile removal problems in the testfile 0.12 2011-12-01 16:58:05 EST5EDT Added: - New functions capture_stdout, capture_stderr, tee_stdout, tee_stderr [rt.cpan.org #60515] - Capture functions also returns the return values from the executed coderef [rt.cpan.org #61794, adapted from patch by Christian Walde] - Capture functions take optional custom filehandles for capturing via named files instead of anonymous ones [inspired by Christian Walde] Fixed: - Tied filehandles based on Tie::StdHandle can now use the ":utf8" layer; removed remaining TODO tests; adds Scalar::Util as a dependency Changed: - When Time::HiRes::usleep is available, tee operations will sleep during the busy-loop waiting for tee processes to be ready [rt.cpan.org #67858] 0.11 2011-05-19 23:34:23 America/New_York Fixed: - Tests will not use Test::Differences version 0.60 or greater 0.10 2011-02-07 07:01:44 EST5EDT Fixed: - Setting PERL_CAPTURE_TINY_TIMEOUT to 0 will disable timeouts 0.09 2011-01-27 23:52:16 EST5EDT Added: - Added support for $ENV{PERL_CAPTURE_TINY_TIMEOUT} to control the timeout period under 'tee'; tests set not to timeout to avoid false FAIL reports on overloaded virtual machine smokers Fixed: - $@ set within a captured block is no longer lost when the capture is completed; likewise, the initial value of $@ is not lost during capture (when no subsequent error occurs) (RT #65139) 0.08 Sun Jun 20 19:13:19 EDT 2010 Fixed: - Exceptions in captured coderef are caught, then handles are restored before the exception is rethrown (RT #58208) 0.07 Sun Jan 24 00:18:45 EST 2010 Fixed: - Changed test for $? preservation to be more portable - Dropped support for Perl 5.8.0 specifically due to excessive bugs. Tests will bail out. (5.6.X is still supported) 0.06 Thu May 7 06:54:53 EDT 2009 Fixed: - On Win32, subprocesses now close themselves on EOF instead of being killed with a signal 0.05_51 Tue Apr 21 07:00:38 EDT 2009 Added: - Support for wide characters on handles opened to utf8 - Support for STDOUT, STDERR or STDIN opened to in-memory files (open to scalar reference) or tied, albeit with some limitations Testing: - Verify that $? is preserved during capture { system(@cmd) }; 0.05 Tue Mar 3 06:56:05 EST 2009 Fixed: - On Win32, increased a delay waiting for buffers to flush to avoid losing final output during tee() 0.04 Wed Feb 25 09:25:27 EST 2009 Added: - Can capture/tee even if STDIN, STDOUT or STDERR are closed prior to capture/tee block - Generally, added more error handling Fixed: - Will timeout instead of hang if subprocesses fail to start 0.03 Fri Feb 20 13:03:08 EST 2009 Added: - capture_merged() and tee_merged() Fixed: - Tests skip if not Win32 and no fork() (rather than Build.PL and Makefile.PL failing); this allows capture() on odd platforms, even if fork doesn't work 0.02 Tue Feb 17 17:24:35 EST 2009 Fixed: - Bug recovering output when STDOUT is empty (reported by Vincent Pit) - Removed Fatal.pm to avoid global action-at-a-distance 0.01 Fri Feb 13 23:15:19 EST 2009 Added: - 'capture' and 'tee' functions # vim: set ts=2 sts=2 sw=2 et tw=75: cpanfile 0000644 00000002776 15043642076 0006273 0 ustar 00 requires "Carp" => "0"; requires "Exporter" => "0"; requires "File::Spec" => "0"; requires "File::Temp" => "0"; requires "IO::Handle" => "0"; requires "Scalar::Util" => "0"; requires "perl" => "5.006"; requires "strict" => "0"; requires "warnings" => "0"; on 'test' => sub { requires "ExtUtils::MakeMaker" => "0"; requires "File::Spec" => "0"; requires "IO::File" => "0"; requires "Test::More" => "0.62"; requires "lib" => "0"; }; on 'test' => sub { recommends "CPAN::Meta" => "2.120900"; }; on 'configure' => sub { requires "ExtUtils::MakeMaker" => "6.17"; }; on 'develop' => sub { requires "Dist::Zilla" => "5"; requires "Dist::Zilla::Plugin::OSPrereqs" => "0"; requires "Dist::Zilla::Plugin::Prereqs" => "0"; requires "Dist::Zilla::Plugin::ReleaseStatus::FromVersion" => "0"; requires "Dist::Zilla::Plugin::RemovePrereqs" => "0"; requires "Dist::Zilla::PluginBundle::DAGOLDEN" => "0.072"; requires "File::Spec" => "0"; requires "File::Temp" => "0"; requires "IO::Handle" => "0"; requires "IPC::Open3" => "0"; requires "Pod::Coverage::TrustPod" => "0"; requires "Pod::Wordlist" => "0"; requires "Software::License::Apache_2_0" => "0"; requires "Test::CPAN::Meta" => "0"; requires "Test::MinimumVersion" => "0"; requires "Test::More" => "0"; requires "Test::Perl::Critic" => "0"; requires "Test::Pod" => "1.41"; requires "Test::Pod::Coverage" => "1.08"; requires "Test::Portability::Files" => "0"; requires "Test::Spelling" => "0.12"; requires "Test::Version" => "1"; }; examples/tee.pl 0000644 00000000621 15043642076 0007502 0 ustar 00 use strict; use warnings; use Capture::Tiny qw/capture tee/; print "Type some text. Type 'exit' to quit\n"; my ($out, $err) = tee { while (<>) { last if /^exit$/; print "Echoing to STDOUT: $_"; print STDERR "Echoing to STDERR: $_"; } }; print "\nCaptured STDOUT was:\n" . ( defined $out ? $out : 'undef' ); print "\nCaptured STDERR was:\n" . ( defined $err ? $err : 'undef' ); examples/rt-58208.pl 0000644 00000000556 15043642076 0010045 0 ustar 00 use Capture::Tiny qw[ capture ]; my ( $out, $err ) = eval { capture { print STDERR "hello\n"; print STDOUT "there\n"; die("foo\n" ) } }; print STDERR "STDERR:\nout=$out\nerr=$err\n\$@=$@"; print STDOUT "STDOUT:\nout=$out\nerr=$err\n\$@=$@"; open FILE, '>ttt.log' or die( "error opening logfile\n" ); print FILE "FILE:\nout=$out\nerr=$err\n\$@=$@\n"; close FILE; Todo 0000644 00000000565 15043642076 0005411 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 - Test utf8 output - Test with curses Makefile 0000644 00000075413 15043642076 0006225 0 ustar 00 # This Makefile is for the Capture::Tiny extension to perl. # # It was generated automatically by MakeMaker version # 7.60 (Revision: 76000) from the contents of # Makefile.PL. Don't edit this file, edit Makefile.PL instead. # # ANY CHANGES MADE HERE WILL BE LOST! # # MakeMaker ARGV: () # # MakeMaker Parameters: # ABSTRACT => q[Capture STDOUT and STDERR from Perl, XS or external programs] # AUTHOR => [q[David Golden <dagolden@cpan.org>]] # BUILD_REQUIRES => { } # CONFIGURE_REQUIRES => { ExtUtils::MakeMaker=>q[6.17] } # DISTNAME => q[Capture-Tiny] # LICENSE => q[apache] # MIN_PERL_VERSION => q[5.006] # NAME => q[Capture::Tiny] # PREREQ_PM => { Carp=>q[0], Exporter=>q[0], ExtUtils::MakeMaker=>q[0], File::Spec=>q[0], File::Temp=>q[0], IO::File=>q[0], IO::Handle=>q[0], Scalar::Util=>q[0], Test::More=>q[0.62], lib=>q[0], strict=>q[0], warnings=>q[0] } # TEST_REQUIRES => { ExtUtils::MakeMaker=>q[0], File::Spec=>q[0], IO::File=>q[0], Test::More=>q[0.62], lib=>q[0] } # VERSION => q[0.48] # test => { TESTS=>q[t/*.t] } # --- MakeMaker post_initialize section: # --- MakeMaker const_config section: # These definitions are from config.sh (via /usr/lib64/perl5/Config.pm). # They may have been overridden via Makefile.PL or on the command line. AR = ar CC = gcc CCCDLFLAGS = -fPIC CCDLFLAGS = -Wl,--enable-new-dtags -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 DLEXT = so DLSRC = dl_dlopen.xs EXE_EXT = FULL_AR = /usr/bin/ar LD = gcc LDDLFLAGS = -lpthread -shared -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -L/usr/local/lib -fstack-protector-strong LDFLAGS = -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -fstack-protector-strong -L/usr/local/lib LIBC = /lib/../lib64/libc.so.6 LIB_EXT = .a OBJ_EXT = .o OSNAME = linux OSVERS = 4.18.0-513.18.1.el8_9.x86_64 RANLIB = : SITELIBEXP = /usr/local/share/perl5/5.32 SITEARCHEXP = /usr/local/lib64/perl5/5.32 SO = so VENDORARCHEXP = /usr/lib64/perl5/vendor_perl VENDORLIBEXP = /usr/share/perl5/vendor_perl # --- MakeMaker constants section: AR_STATIC_ARGS = cr DIRFILESEP = / DFSEP = $(DIRFILESEP) NAME = Capture::Tiny NAME_SYM = Capture_Tiny VERSION = 0.48 VERSION_MACRO = VERSION VERSION_SYM = 0_48 DEFINE_VERSION = -D$(VERSION_MACRO)=\"$(VERSION)\" XS_VERSION = 0.48 XS_VERSION_MACRO = XS_VERSION XS_DEFINE_VERSION = -D$(XS_VERSION_MACRO)=\"$(XS_VERSION)\" INST_ARCHLIB = blib/arch INST_SCRIPT = blib/script INST_BIN = blib/bin INST_LIB = blib/lib INST_MAN1DIR = blib/man1 INST_MAN3DIR = blib/man3 MAN1EXT = 1 MAN3EXT = 3pm MAN1SECTION = 1 MAN3SECTION = 3 INSTALLDIRS = site DESTDIR = PREFIX = $(SITEPREFIX) PERLPREFIX = /usr SITEPREFIX = /usr/local VENDORPREFIX = /usr INSTALLPRIVLIB = /usr/share/perl5 DESTINSTALLPRIVLIB = $(DESTDIR)$(INSTALLPRIVLIB) INSTALLSITELIB = /usr/local/share/perl5/5.32 DESTINSTALLSITELIB = $(DESTDIR)$(INSTALLSITELIB) INSTALLVENDORLIB = /usr/share/perl5/vendor_perl DESTINSTALLVENDORLIB = $(DESTDIR)$(INSTALLVENDORLIB) INSTALLARCHLIB = /usr/lib64/perl5 DESTINSTALLARCHLIB = $(DESTDIR)$(INSTALLARCHLIB) INSTALLSITEARCH = /usr/local/lib64/perl5/5.32 DESTINSTALLSITEARCH = $(DESTDIR)$(INSTALLSITEARCH) INSTALLVENDORARCH = /usr/lib64/perl5/vendor_perl DESTINSTALLVENDORARCH = $(DESTDIR)$(INSTALLVENDORARCH) INSTALLBIN = /usr/bin DESTINSTALLBIN = $(DESTDIR)$(INSTALLBIN) INSTALLSITEBIN = /usr/local/bin DESTINSTALLSITEBIN = $(DESTDIR)$(INSTALLSITEBIN) INSTALLVENDORBIN = /usr/bin DESTINSTALLVENDORBIN = $(DESTDIR)$(INSTALLVENDORBIN) INSTALLSCRIPT = /usr/bin DESTINSTALLSCRIPT = $(DESTDIR)$(INSTALLSCRIPT) INSTALLSITESCRIPT = /usr/local/bin DESTINSTALLSITESCRIPT = $(DESTDIR)$(INSTALLSITESCRIPT) INSTALLVENDORSCRIPT = /usr/bin DESTINSTALLVENDORSCRIPT = $(DESTDIR)$(INSTALLVENDORSCRIPT) INSTALLMAN1DIR = /usr/share/man/man1 DESTINSTALLMAN1DIR = $(DESTDIR)$(INSTALLMAN1DIR) INSTALLSITEMAN1DIR = /usr/local/share/man/man1 DESTINSTALLSITEMAN1DIR = $(DESTDIR)$(INSTALLSITEMAN1DIR) INSTALLVENDORMAN1DIR = /usr/share/man/man1 DESTINSTALLVENDORMAN1DIR = $(DESTDIR)$(INSTALLVENDORMAN1DIR) INSTALLMAN3DIR = /usr/share/man/man3 DESTINSTALLMAN3DIR = $(DESTDIR)$(INSTALLMAN3DIR) INSTALLSITEMAN3DIR = /usr/local/share/man/man3 DESTINSTALLSITEMAN3DIR = $(DESTDIR)$(INSTALLSITEMAN3DIR) INSTALLVENDORMAN3DIR = /usr/share/man/man3 DESTINSTALLVENDORMAN3DIR = $(DESTDIR)$(INSTALLVENDORMAN3DIR) PERL_LIB = /usr/share/perl5 PERL_ARCHLIB = /usr/lib64/perl5 PERL_ARCHLIBDEP = /usr/lib64/perl5 LIBPERL_A = libperl.a FIRST_MAKEFILE = Makefile MAKEFILE_OLD = Makefile.old MAKE_APERL_FILE = Makefile.aperl PERLMAINCC = $(CC) PERL_INC = /usr/lib64/perl5/CORE PERL_INCDEP = /usr/lib64/perl5/CORE PERL = "/usr/bin/perl" FULLPERL = "/usr/bin/perl" ABSPERL = $(PERL) PERLRUN = $(PERL) FULLPERLRUN = $(FULLPERL) ABSPERLRUN = $(ABSPERL) PERLRUNINST = $(PERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)" FULLPERLRUNINST = $(FULLPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)" ABSPERLRUNINST = $(ABSPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)" PERL_CORE = 0 PERM_DIR = 755 PERM_RW = 644 PERM_RWX = 755 MAKEMAKER = /usr/share/perl5/vendor_perl/ExtUtils/MakeMaker.pm MM_VERSION = 7.60 MM_REVISION = 76000 # FULLEXT = Pathname for extension directory (eg Foo/Bar/Oracle). # BASEEXT = Basename part of FULLEXT. May be just equal FULLEXT. (eg Oracle) # PARENT_NAME = NAME without BASEEXT and no trailing :: (eg Foo::Bar) # DLBASE = Basename part of dynamic library. May be just equal BASEEXT. MAKE = make FULLEXT = Capture/Tiny BASEEXT = Tiny PARENT_NAME = Capture DLBASE = $(BASEEXT) VERSION_FROM = OBJECT = LDFROM = $(OBJECT) LINKTYPE = dynamic BOOTDEP = # Handy lists of source code files: XS_FILES = C_FILES = O_FILES = H_FILES = MAN1PODS = MAN3PODS = lib/Capture/Tiny.pm # Where is the Config information that we are using/depend on CONFIGDEP = $(PERL_ARCHLIBDEP)$(DFSEP)Config.pm $(PERL_INCDEP)$(DFSEP)config.h # Where to build things INST_LIBDIR = $(INST_LIB)/Capture INST_ARCHLIBDIR = $(INST_ARCHLIB)/Capture INST_AUTODIR = $(INST_LIB)/auto/$(FULLEXT) INST_ARCHAUTODIR = $(INST_ARCHLIB)/auto/$(FULLEXT) INST_STATIC = INST_DYNAMIC = INST_BOOT = # Extra linker info EXPORT_LIST = PERL_ARCHIVE = PERL_ARCHIVEDEP = PERL_ARCHIVE_AFTER = TO_INST_PM = lib/Capture/Tiny.pm # --- MakeMaker platform_constants section: MM_Unix_VERSION = 7.60 PERL_MALLOC_DEF = -DPERL_EXTMALLOC_DEF -Dmalloc=Perl_malloc -Dfree=Perl_mfree -Drealloc=Perl_realloc -Dcalloc=Perl_calloc # --- MakeMaker tool_autosplit section: # Usage: $(AUTOSPLITFILE) FileToSplit AutoDirToSplitInto AUTOSPLITFILE = $(ABSPERLRUN) -e 'use AutoSplit; autosplit($$$$ARGV[0], $$$$ARGV[1], 0, 1, 1)' -- # --- MakeMaker tool_xsubpp section: # --- MakeMaker tools_other section: SHELL = /bin/sh CHMOD = chmod CP = cp MV = mv NOOP = $(TRUE) NOECHO = @ RM_F = rm -f RM_RF = rm -rf TEST_F = test -f TOUCH = touch UMASK_NULL = umask 0 DEV_NULL = > /dev/null 2>&1 MKPATH = $(ABSPERLRUN) -MExtUtils::Command -e 'mkpath' -- EQUALIZE_TIMESTAMP = $(ABSPERLRUN) -MExtUtils::Command -e 'eqtime' -- FALSE = false TRUE = true ECHO = echo ECHO_N = echo -n UNINST = 0 VERBINST = 0 MOD_INSTALL = $(ABSPERLRUN) -MExtUtils::Install -e 'install([ from_to => {@ARGV}, verbose => '\''$(VERBINST)'\'', uninstall_shadows => '\''$(UNINST)'\'', dir_mode => '\''$(PERM_DIR)'\'' ]);' -- DOC_INSTALL = $(ABSPERLRUN) -MExtUtils::Command::MM -e 'perllocal_install' -- UNINSTALL = $(ABSPERLRUN) -MExtUtils::Command::MM -e 'uninstall' -- WARN_IF_OLD_PACKLIST = $(ABSPERLRUN) -MExtUtils::Command::MM -e 'warn_if_old_packlist' -- MACROSTART = MACROEND = USEMAKEFILE = -f FIXIN = $(ABSPERLRUN) -MExtUtils::MY -e 'MY->fixin(shift)' -- CP_NONEMPTY = $(ABSPERLRUN) -MExtUtils::Command::MM -e 'cp_nonempty' -- # --- MakeMaker makemakerdflt section: makemakerdflt : all $(NOECHO) $(NOOP) # --- MakeMaker dist section: TAR = tar TARFLAGS = cvf ZIP = zip ZIPFLAGS = -r COMPRESS = gzip --best SUFFIX = .gz SHAR = shar PREOP = $(NOECHO) $(NOOP) POSTOP = $(NOECHO) $(NOOP) TO_UNIX = $(NOECHO) $(NOOP) CI = ci -u RCS_LABEL = rcs -Nv$(VERSION_SYM): -q DIST_CP = best DIST_DEFAULT = tardist DISTNAME = Capture-Tiny DISTVNAME = Capture-Tiny-0.48 # --- MakeMaker macro section: # --- MakeMaker depend section: # --- MakeMaker cflags section: # --- MakeMaker const_loadlibs section: # --- MakeMaker const_cccmd section: # --- MakeMaker post_constants section: # --- MakeMaker pasthru section: PASTHRU = LIBPERL_A="$(LIBPERL_A)"\ LINKTYPE="$(LINKTYPE)"\ PREFIX="$(PREFIX)"\ PASTHRU_DEFINE='$(DEFINE) $(PASTHRU_DEFINE)'\ PASTHRU_INC='$(INC) $(PASTHRU_INC)' # --- MakeMaker special_targets section: .SUFFIXES : .xs .c .C .cpp .i .s .cxx .cc $(OBJ_EXT) .PHONY: all config static dynamic test linkext manifest blibdirs clean realclean disttest distdir pure_all subdirs clean_subdirs makemakerdflt manifypods realclean_subdirs subdirs_dynamic subdirs_pure_nolink subdirs_static subdirs-test_dynamic subdirs-test_static test_dynamic test_static # --- MakeMaker c_o section: # --- MakeMaker xs_c section: # --- MakeMaker xs_o section: # --- MakeMaker top_targets section: all :: pure_all manifypods $(NOECHO) $(NOOP) pure_all :: config pm_to_blib subdirs linkext $(NOECHO) $(NOOP) subdirs :: $(MYEXTLIB) $(NOECHO) $(NOOP) config :: $(FIRST_MAKEFILE) blibdirs $(NOECHO) $(NOOP) help : perldoc ExtUtils::MakeMaker # --- MakeMaker blibdirs section: blibdirs : $(INST_LIBDIR)$(DFSEP).exists $(INST_ARCHLIB)$(DFSEP).exists $(INST_AUTODIR)$(DFSEP).exists $(INST_ARCHAUTODIR)$(DFSEP).exists $(INST_BIN)$(DFSEP).exists $(INST_SCRIPT)$(DFSEP).exists $(INST_MAN1DIR)$(DFSEP).exists $(INST_MAN3DIR)$(DFSEP).exists $(NOECHO) $(NOOP) # Backwards compat with 6.18 through 6.25 blibdirs.ts : blibdirs $(NOECHO) $(NOOP) $(INST_LIBDIR)$(DFSEP).exists :: Makefile.PL $(NOECHO) $(MKPATH) $(INST_LIBDIR) $(NOECHO) $(CHMOD) $(PERM_DIR) $(INST_LIBDIR) $(NOECHO) $(TOUCH) $(INST_LIBDIR)$(DFSEP).exists $(INST_ARCHLIB)$(DFSEP).exists :: Makefile.PL $(NOECHO) $(MKPATH) $(INST_ARCHLIB) $(NOECHO) $(CHMOD) $(PERM_DIR) $(INST_ARCHLIB) $(NOECHO) $(TOUCH) $(INST_ARCHLIB)$(DFSEP).exists $(INST_AUTODIR)$(DFSEP).exists :: Makefile.PL $(NOECHO) $(MKPATH) $(INST_AUTODIR) $(NOECHO) $(CHMOD) $(PERM_DIR) $(INST_AUTODIR) $(NOECHO) $(TOUCH) $(INST_AUTODIR)$(DFSEP).exists $(INST_ARCHAUTODIR)$(DFSEP).exists :: Makefile.PL $(NOECHO) $(MKPATH) $(INST_ARCHAUTODIR) $(NOECHO) $(CHMOD) $(PERM_DIR) $(INST_ARCHAUTODIR) $(NOECHO) $(TOUCH) $(INST_ARCHAUTODIR)$(DFSEP).exists $(INST_BIN)$(DFSEP).exists :: Makefile.PL $(NOECHO) $(MKPATH) $(INST_BIN) $(NOECHO) $(CHMOD) $(PERM_DIR) $(INST_BIN) $(NOECHO) $(TOUCH) $(INST_BIN)$(DFSEP).exists $(INST_SCRIPT)$(DFSEP).exists :: Makefile.PL $(NOECHO) $(MKPATH) $(INST_SCRIPT) $(NOECHO) $(CHMOD) $(PERM_DIR) $(INST_SCRIPT) $(NOECHO) $(TOUCH) $(INST_SCRIPT)$(DFSEP).exists $(INST_MAN1DIR)$(DFSEP).exists :: Makefile.PL $(NOECHO) $(MKPATH) $(INST_MAN1DIR) $(NOECHO) $(CHMOD) $(PERM_DIR) $(INST_MAN1DIR) $(NOECHO) $(TOUCH) $(INST_MAN1DIR)$(DFSEP).exists $(INST_MAN3DIR)$(DFSEP).exists :: Makefile.PL $(NOECHO) $(MKPATH) $(INST_MAN3DIR) $(NOECHO) $(CHMOD) $(PERM_DIR) $(INST_MAN3DIR) $(NOECHO) $(TOUCH) $(INST_MAN3DIR)$(DFSEP).exists # --- MakeMaker linkext section: linkext :: dynamic $(NOECHO) $(NOOP) # --- MakeMaker dlsyms section: # --- MakeMaker dynamic_bs section: BOOTSTRAP = # --- MakeMaker dynamic section: dynamic :: $(FIRST_MAKEFILE) config $(INST_BOOT) $(INST_DYNAMIC) $(NOECHO) $(NOOP) # --- MakeMaker dynamic_lib section: # --- MakeMaker static section: ## $(INST_PM) has been moved to the all: target. ## It remains here for awhile to allow for old usage: "make static" static :: $(FIRST_MAKEFILE) $(INST_STATIC) $(NOECHO) $(NOOP) # --- MakeMaker static_lib section: # --- MakeMaker manifypods section: POD2MAN_EXE = $(PERLRUN) "-MExtUtils::Command::MM" -e pod2man "--" POD2MAN = $(POD2MAN_EXE) manifypods : pure_all config \ lib/Capture/Tiny.pm $(NOECHO) $(POD2MAN) --section=$(MAN3SECTION) --perm_rw=$(PERM_RW) -u \ lib/Capture/Tiny.pm $(INST_MAN3DIR)/Capture::Tiny.$(MAN3EXT) # --- MakeMaker processPL section: # --- MakeMaker installbin section: # --- MakeMaker subdirs section: # none # --- MakeMaker clean_subdirs section: clean_subdirs : $(NOECHO) $(NOOP) # --- MakeMaker clean section: # Delete temporary files but do not touch installed files. We don't delete # the Makefile here so a later make realclean still has a makefile to use. clean :: clean_subdirs - $(RM_F) \ $(BASEEXT).bso $(BASEEXT).def \ $(BASEEXT).exp $(BASEEXT).x \ $(BOOTSTRAP) $(INST_ARCHAUTODIR)/extralibs.all \ $(INST_ARCHAUTODIR)/extralibs.ld $(MAKE_APERL_FILE) \ *$(LIB_EXT) *$(OBJ_EXT) \ *perl.core MYMETA.json \ MYMETA.yml blibdirs.ts \ core core.*perl.*.? \ core.[0-9] core.[0-9][0-9] \ core.[0-9][0-9][0-9] core.[0-9][0-9][0-9][0-9] \ core.[0-9][0-9][0-9][0-9][0-9] lib$(BASEEXT).def \ mon.out perl \ perl$(EXE_EXT) perl.exe \ perlmain.c pm_to_blib \ pm_to_blib.ts so_locations \ tmon.out - $(RM_RF) \ blib $(NOECHO) $(RM_F) $(MAKEFILE_OLD) - $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD) $(DEV_NULL) # --- MakeMaker realclean_subdirs section: # so clean is forced to complete before realclean_subdirs runs realclean_subdirs : clean $(NOECHO) $(NOOP) # --- MakeMaker realclean section: # Delete temporary files (via clean) and also delete dist files realclean purge :: realclean_subdirs - $(RM_F) \ $(FIRST_MAKEFILE) $(MAKEFILE_OLD) - $(RM_RF) \ $(DISTVNAME) # --- MakeMaker metafile section: metafile : create_distdir $(NOECHO) $(ECHO) Generating META.yml $(NOECHO) $(ECHO) '---' > META_new.yml $(NOECHO) $(ECHO) 'abstract: '\''Capture STDOUT and STDERR from Perl, XS or external programs'\''' >> META_new.yml $(NOECHO) $(ECHO) 'author:' >> META_new.yml $(NOECHO) $(ECHO) ' - '\''David Golden <dagolden@cpan.org>'\''' >> META_new.yml $(NOECHO) $(ECHO) 'build_requires:' >> META_new.yml $(NOECHO) $(ECHO) ' ExtUtils::MakeMaker: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' File::Spec: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' IO::File: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' Test::More: '\''0.62'\''' >> META_new.yml $(NOECHO) $(ECHO) ' lib: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) 'configure_requires:' >> META_new.yml $(NOECHO) $(ECHO) ' ExtUtils::MakeMaker: '\''6.17'\''' >> META_new.yml $(NOECHO) $(ECHO) 'dynamic_config: 1' >> META_new.yml $(NOECHO) $(ECHO) 'generated_by: '\''ExtUtils::MakeMaker version 7.60, CPAN::Meta::Converter version 2.150010'\''' >> META_new.yml $(NOECHO) $(ECHO) 'license: apache' >> META_new.yml $(NOECHO) $(ECHO) 'meta-spec:' >> META_new.yml $(NOECHO) $(ECHO) ' url: http://module-build.sourceforge.net/META-spec-v1.4.html' >> META_new.yml $(NOECHO) $(ECHO) ' version: '\''1.4'\''' >> META_new.yml $(NOECHO) $(ECHO) 'name: Capture-Tiny' >> META_new.yml $(NOECHO) $(ECHO) 'no_index:' >> META_new.yml $(NOECHO) $(ECHO) ' directory:' >> META_new.yml $(NOECHO) $(ECHO) ' - t' >> META_new.yml $(NOECHO) $(ECHO) ' - inc' >> META_new.yml $(NOECHO) $(ECHO) 'requires:' >> META_new.yml $(NOECHO) $(ECHO) ' Carp: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' Exporter: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' File::Spec: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' File::Temp: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' IO::Handle: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' Scalar::Util: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' perl: '\''5.006'\''' >> META_new.yml $(NOECHO) $(ECHO) ' strict: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) ' warnings: '\''0'\''' >> META_new.yml $(NOECHO) $(ECHO) 'version: '\''0.48'\''' >> META_new.yml $(NOECHO) $(ECHO) 'x_serialization_backend: '\''CPAN::Meta::YAML version 0.018'\''' >> META_new.yml -$(NOECHO) $(MV) META_new.yml $(DISTVNAME)/META.yml $(NOECHO) $(ECHO) Generating META.json $(NOECHO) $(ECHO) '{' > META_new.json $(NOECHO) $(ECHO) ' "abstract" : "Capture STDOUT and STDERR from Perl, XS or external programs",' >> META_new.json $(NOECHO) $(ECHO) ' "author" : [' >> META_new.json $(NOECHO) $(ECHO) ' "David Golden <dagolden@cpan.org>"' >> META_new.json $(NOECHO) $(ECHO) ' ],' >> META_new.json $(NOECHO) $(ECHO) ' "dynamic_config" : 1,' >> META_new.json $(NOECHO) $(ECHO) ' "generated_by" : "ExtUtils::MakeMaker version 7.60, CPAN::Meta::Converter version 2.150010",' >> META_new.json $(NOECHO) $(ECHO) ' "license" : [' >> META_new.json $(NOECHO) $(ECHO) ' "apache_2_0"' >> META_new.json $(NOECHO) $(ECHO) ' ],' >> META_new.json $(NOECHO) $(ECHO) ' "meta-spec" : {' >> META_new.json $(NOECHO) $(ECHO) ' "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",' >> META_new.json $(NOECHO) $(ECHO) ' "version" : 2' >> META_new.json $(NOECHO) $(ECHO) ' },' >> META_new.json $(NOECHO) $(ECHO) ' "name" : "Capture-Tiny",' >> META_new.json $(NOECHO) $(ECHO) ' "no_index" : {' >> META_new.json $(NOECHO) $(ECHO) ' "directory" : [' >> META_new.json $(NOECHO) $(ECHO) ' "t",' >> META_new.json $(NOECHO) $(ECHO) ' "inc"' >> META_new.json $(NOECHO) $(ECHO) ' ]' >> META_new.json $(NOECHO) $(ECHO) ' },' >> META_new.json $(NOECHO) $(ECHO) ' "prereqs" : {' >> META_new.json $(NOECHO) $(ECHO) ' "build" : {' >> META_new.json $(NOECHO) $(ECHO) ' "requires" : {' >> META_new.json $(NOECHO) $(ECHO) ' "ExtUtils::MakeMaker" : "0"' >> META_new.json $(NOECHO) $(ECHO) ' }' >> META_new.json $(NOECHO) $(ECHO) ' },' >> META_new.json $(NOECHO) $(ECHO) ' "configure" : {' >> META_new.json $(NOECHO) $(ECHO) ' "requires" : {' >> META_new.json $(NOECHO) $(ECHO) ' "ExtUtils::MakeMaker" : "6.17"' >> META_new.json $(NOECHO) $(ECHO) ' }' >> META_new.json $(NOECHO) $(ECHO) ' },' >> META_new.json $(NOECHO) $(ECHO) ' "runtime" : {' >> META_new.json $(NOECHO) $(ECHO) ' "requires" : {' >> META_new.json $(NOECHO) $(ECHO) ' "Carp" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "Exporter" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "File::Spec" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "File::Temp" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "IO::Handle" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "Scalar::Util" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "perl" : "5.006",' >> META_new.json $(NOECHO) $(ECHO) ' "strict" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "warnings" : "0"' >> META_new.json $(NOECHO) $(ECHO) ' }' >> META_new.json $(NOECHO) $(ECHO) ' },' >> META_new.json $(NOECHO) $(ECHO) ' "test" : {' >> META_new.json $(NOECHO) $(ECHO) ' "requires" : {' >> META_new.json $(NOECHO) $(ECHO) ' "ExtUtils::MakeMaker" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "File::Spec" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "IO::File" : "0",' >> META_new.json $(NOECHO) $(ECHO) ' "Test::More" : "0.62",' >> META_new.json $(NOECHO) $(ECHO) ' "lib" : "0"' >> META_new.json $(NOECHO) $(ECHO) ' }' >> META_new.json $(NOECHO) $(ECHO) ' }' >> META_new.json $(NOECHO) $(ECHO) ' },' >> META_new.json $(NOECHO) $(ECHO) ' "release_status" : "stable",' >> META_new.json $(NOECHO) $(ECHO) ' "version" : "0.48",' >> META_new.json $(NOECHO) $(ECHO) ' "x_serialization_backend" : "JSON::PP version 4.06"' >> META_new.json $(NOECHO) $(ECHO) '}' >> META_new.json -$(NOECHO) $(MV) META_new.json $(DISTVNAME)/META.json # --- MakeMaker signature section: signature : cpansign -s # --- MakeMaker dist_basics section: distclean :: realclean distcheck $(NOECHO) $(NOOP) distcheck : $(PERLRUN) "-MExtUtils::Manifest=fullcheck" -e fullcheck skipcheck : $(PERLRUN) "-MExtUtils::Manifest=skipcheck" -e skipcheck manifest : $(PERLRUN) "-MExtUtils::Manifest=mkmanifest" -e mkmanifest veryclean : realclean $(RM_F) *~ */*~ *.orig */*.orig *.bak */*.bak *.old */*.old # --- MakeMaker dist_core section: dist : $(DIST_DEFAULT) $(FIRST_MAKEFILE) $(NOECHO) $(ABSPERLRUN) -l -e 'print '\''Warning: Makefile possibly out of date with $(VERSION_FROM)'\''' \ -e ' if -e '\''$(VERSION_FROM)'\'' and -M '\''$(VERSION_FROM)'\'' < -M '\''$(FIRST_MAKEFILE)'\'';' -- tardist : $(DISTVNAME).tar$(SUFFIX) $(NOECHO) $(NOOP) uutardist : $(DISTVNAME).tar$(SUFFIX) uuencode $(DISTVNAME).tar$(SUFFIX) $(DISTVNAME).tar$(SUFFIX) > $(DISTVNAME).tar$(SUFFIX)_uu $(NOECHO) $(ECHO) 'Created $(DISTVNAME).tar$(SUFFIX)_uu' $(DISTVNAME).tar$(SUFFIX) : distdir $(PREOP) $(TO_UNIX) $(TAR) $(TARFLAGS) $(DISTVNAME).tar $(DISTVNAME) $(RM_RF) $(DISTVNAME) $(COMPRESS) $(DISTVNAME).tar $(NOECHO) $(ECHO) 'Created $(DISTVNAME).tar$(SUFFIX)' $(POSTOP) zipdist : $(DISTVNAME).zip $(NOECHO) $(NOOP) $(DISTVNAME).zip : distdir $(PREOP) $(ZIP) $(ZIPFLAGS) $(DISTVNAME).zip $(DISTVNAME) $(RM_RF) $(DISTVNAME) $(NOECHO) $(ECHO) 'Created $(DISTVNAME).zip' $(POSTOP) shdist : distdir $(PREOP) $(SHAR) $(DISTVNAME) > $(DISTVNAME).shar $(RM_RF) $(DISTVNAME) $(NOECHO) $(ECHO) 'Created $(DISTVNAME).shar' $(POSTOP) # --- MakeMaker distdir section: create_distdir : $(RM_RF) $(DISTVNAME) $(PERLRUN) "-MExtUtils::Manifest=manicopy,maniread" \ -e "manicopy(maniread(),'$(DISTVNAME)', '$(DIST_CP)');" distdir : create_distdir distmeta $(NOECHO) $(NOOP) # --- MakeMaker dist_test section: disttest : distdir cd $(DISTVNAME) && $(ABSPERLRUN) Makefile.PL cd $(DISTVNAME) && $(MAKE) $(PASTHRU) cd $(DISTVNAME) && $(MAKE) test $(PASTHRU) # --- MakeMaker dist_ci section: ci : $(ABSPERLRUN) -MExtUtils::Manifest=maniread -e '@all = sort keys %{ maniread() };' \ -e 'print(qq{Executing $(CI) @all\n});' \ -e 'system(qq{$(CI) @all}) == 0 or die $$!;' \ -e 'print(qq{Executing $(RCS_LABEL) ...\n});' \ -e 'system(qq{$(RCS_LABEL) @all}) == 0 or die $$!;' -- # --- MakeMaker distmeta section: distmeta : create_distdir metafile $(NOECHO) cd $(DISTVNAME) && $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'exit unless -e q{META.yml};' \ -e 'eval { maniadd({q{META.yml} => q{Module YAML meta-data (added by MakeMaker)}}) }' \ -e ' or die "Could not add META.yml to MANIFEST: $${'\''@'\''}"' -- $(NOECHO) cd $(DISTVNAME) && $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'exit unless -f q{META.json};' \ -e 'eval { maniadd({q{META.json} => q{Module JSON meta-data (added by MakeMaker)}}) }' \ -e ' or die "Could not add META.json to MANIFEST: $${'\''@'\''}"' -- # --- MakeMaker distsignature section: distsignature : distmeta $(NOECHO) cd $(DISTVNAME) && $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'eval { maniadd({q{SIGNATURE} => q{Public-key signature (added by MakeMaker)}}) }' \ -e ' or die "Could not add SIGNATURE to MANIFEST: $${'\''@'\''}"' -- $(NOECHO) cd $(DISTVNAME) && $(TOUCH) SIGNATURE cd $(DISTVNAME) && cpansign -s # --- MakeMaker install section: install :: pure_install doc_install $(NOECHO) $(NOOP) install_perl :: pure_perl_install doc_perl_install $(NOECHO) $(NOOP) install_site :: pure_site_install doc_site_install $(NOECHO) $(NOOP) install_vendor :: pure_vendor_install doc_vendor_install $(NOECHO) $(NOOP) pure_install :: pure_$(INSTALLDIRS)_install $(NOECHO) $(NOOP) doc_install :: doc_$(INSTALLDIRS)_install $(NOECHO) $(NOOP) pure__install : pure_site_install $(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site doc__install : doc_site_install $(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site pure_perl_install :: all $(NOECHO) $(MOD_INSTALL) \ read "$(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist" \ write "$(DESTINSTALLARCHLIB)/auto/$(FULLEXT)/.packlist" \ "$(INST_LIB)" "$(DESTINSTALLPRIVLIB)" \ "$(INST_ARCHLIB)" "$(DESTINSTALLARCHLIB)" \ "$(INST_BIN)" "$(DESTINSTALLBIN)" \ "$(INST_SCRIPT)" "$(DESTINSTALLSCRIPT)" \ "$(INST_MAN1DIR)" "$(DESTINSTALLMAN1DIR)" \ "$(INST_MAN3DIR)" "$(DESTINSTALLMAN3DIR)" $(NOECHO) $(WARN_IF_OLD_PACKLIST) \ "$(SITEARCHEXP)/auto/$(FULLEXT)" pure_site_install :: all $(NOECHO) $(MOD_INSTALL) \ read "$(SITEARCHEXP)/auto/$(FULLEXT)/.packlist" \ write "$(DESTINSTALLSITEARCH)/auto/$(FULLEXT)/.packlist" \ "$(INST_LIB)" "$(DESTINSTALLSITELIB)" \ "$(INST_ARCHLIB)" "$(DESTINSTALLSITEARCH)" \ "$(INST_BIN)" "$(DESTINSTALLSITEBIN)" \ "$(INST_SCRIPT)" "$(DESTINSTALLSITESCRIPT)" \ "$(INST_MAN1DIR)" "$(DESTINSTALLSITEMAN1DIR)" \ "$(INST_MAN3DIR)" "$(DESTINSTALLSITEMAN3DIR)" $(NOECHO) $(WARN_IF_OLD_PACKLIST) \ "$(PERL_ARCHLIB)/auto/$(FULLEXT)" pure_vendor_install :: all $(NOECHO) $(MOD_INSTALL) \ read "$(VENDORARCHEXP)/auto/$(FULLEXT)/.packlist" \ write "$(DESTINSTALLVENDORARCH)/auto/$(FULLEXT)/.packlist" \ "$(INST_LIB)" "$(DESTINSTALLVENDORLIB)" \ "$(INST_ARCHLIB)" "$(DESTINSTALLVENDORARCH)" \ "$(INST_BIN)" "$(DESTINSTALLVENDORBIN)" \ "$(INST_SCRIPT)" "$(DESTINSTALLVENDORSCRIPT)" \ "$(INST_MAN1DIR)" "$(DESTINSTALLVENDORMAN1DIR)" \ "$(INST_MAN3DIR)" "$(DESTINSTALLVENDORMAN3DIR)" doc_perl_install :: all $(NOECHO) $(ECHO) Appending installation info to "$(DESTINSTALLARCHLIB)/perllocal.pod" -$(NOECHO) $(MKPATH) "$(DESTINSTALLARCHLIB)" -$(NOECHO) $(DOC_INSTALL) \ "Module" "$(NAME)" \ "installed into" "$(INSTALLPRIVLIB)" \ LINKTYPE "$(LINKTYPE)" \ VERSION "$(VERSION)" \ EXE_FILES "$(EXE_FILES)" \ >> "$(DESTINSTALLARCHLIB)/perllocal.pod" doc_site_install :: all $(NOECHO) $(ECHO) Appending installation info to "$(DESTINSTALLARCHLIB)/perllocal.pod" -$(NOECHO) $(MKPATH) "$(DESTINSTALLARCHLIB)" -$(NOECHO) $(DOC_INSTALL) \ "Module" "$(NAME)" \ "installed into" "$(INSTALLSITELIB)" \ LINKTYPE "$(LINKTYPE)" \ VERSION "$(VERSION)" \ EXE_FILES "$(EXE_FILES)" \ >> "$(DESTINSTALLARCHLIB)/perllocal.pod" doc_vendor_install :: all $(NOECHO) $(ECHO) Appending installation info to "$(DESTINSTALLARCHLIB)/perllocal.pod" -$(NOECHO) $(MKPATH) "$(DESTINSTALLARCHLIB)" -$(NOECHO) $(DOC_INSTALL) \ "Module" "$(NAME)" \ "installed into" "$(INSTALLVENDORLIB)" \ LINKTYPE "$(LINKTYPE)" \ VERSION "$(VERSION)" \ EXE_FILES "$(EXE_FILES)" \ >> "$(DESTINSTALLARCHLIB)/perllocal.pod" uninstall :: uninstall_from_$(INSTALLDIRS)dirs $(NOECHO) $(NOOP) uninstall_from_perldirs :: $(NOECHO) $(UNINSTALL) "$(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist" uninstall_from_sitedirs :: $(NOECHO) $(UNINSTALL) "$(SITEARCHEXP)/auto/$(FULLEXT)/.packlist" uninstall_from_vendordirs :: $(NOECHO) $(UNINSTALL) "$(VENDORARCHEXP)/auto/$(FULLEXT)/.packlist" # --- MakeMaker force section: # Phony target to force checking subdirectories. FORCE : $(NOECHO) $(NOOP) # --- MakeMaker perldepend section: # --- MakeMaker makefile section: # We take a very conservative approach here, but it's worth it. # We move Makefile to Makefile.old here to avoid gnu make looping. $(FIRST_MAKEFILE) : Makefile.PL $(CONFIGDEP) $(NOECHO) $(ECHO) "Makefile out-of-date with respect to $?" $(NOECHO) $(ECHO) "Cleaning current config before rebuilding Makefile..." -$(NOECHO) $(RM_F) $(MAKEFILE_OLD) -$(NOECHO) $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD) - $(MAKE) $(USEMAKEFILE) $(MAKEFILE_OLD) clean $(DEV_NULL) $(PERLRUN) Makefile.PL $(NOECHO) $(ECHO) "==> Your Makefile has been rebuilt. <==" $(NOECHO) $(ECHO) "==> Please rerun the $(MAKE) command. <==" $(FALSE) # --- MakeMaker staticmake section: # --- MakeMaker makeaperl section --- MAP_TARGET = perl FULLPERL = "/usr/bin/perl" MAP_PERLINC = "-Iblib/arch" "-Iblib/lib" "-I/usr/lib64/perl5" "-I/usr/share/perl5" $(MAP_TARGET) :: $(MAKE_APERL_FILE) $(MAKE) $(USEMAKEFILE) $(MAKE_APERL_FILE) $@ $(MAKE_APERL_FILE) : static $(FIRST_MAKEFILE) pm_to_blib $(NOECHO) $(ECHO) Writing \"$(MAKE_APERL_FILE)\" for this $(MAP_TARGET) $(NOECHO) $(PERLRUNINST) \ Makefile.PL DIR="" \ MAKEFILE=$(MAKE_APERL_FILE) LINKTYPE=static \ MAKEAPERL=1 NORECURS=1 CCCDLFLAGS= # --- MakeMaker test section: TEST_VERBOSE=0 TEST_TYPE=test_$(LINKTYPE) TEST_FILE = test.pl TEST_FILES = t/*.t TESTDB_SW = -d testdb :: testdb_$(LINKTYPE) $(NOECHO) $(NOOP) test :: $(TEST_TYPE) $(NOECHO) $(NOOP) # Occasionally we may face this degenerate target: test_ : test_dynamic $(NOECHO) $(NOOP) subdirs-test_dynamic :: dynamic pure_all test_dynamic :: subdirs-test_dynamic PERL_DL_NONLAZY=1 $(FULLPERLRUN) "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness($(TEST_VERBOSE), '$(INST_LIB)', '$(INST_ARCHLIB)')" $(TEST_FILES) testdb_dynamic :: dynamic pure_all PERL_DL_NONLAZY=1 $(FULLPERLRUN) $(TESTDB_SW) "-I$(INST_LIB)" "-I$(INST_ARCHLIB)" $(TEST_FILE) subdirs-test_static :: static pure_all test_static :: subdirs-test_static PERL_DL_NONLAZY=1 $(FULLPERLRUN) "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness($(TEST_VERBOSE), '$(INST_LIB)', '$(INST_ARCHLIB)')" $(TEST_FILES) testdb_static :: static pure_all PERL_DL_NONLAZY=1 $(FULLPERLRUN) $(TESTDB_SW) "-I$(INST_LIB)" "-I$(INST_ARCHLIB)" $(TEST_FILE) # --- MakeMaker ppd section: # Creates a PPD (Perl Package Description) for a binary distribution. ppd : $(NOECHO) $(ECHO) '<SOFTPKG NAME="Capture-Tiny" VERSION="0.48">' > Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <ABSTRACT>Capture STDOUT and STDERR from Perl, XS or external programs</ABSTRACT>' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <AUTHOR>David Golden <dagolden@cpan.org></AUTHOR>' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <IMPLEMENTATION>' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <PERLCORE VERSION="5,006,0,0" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <REQUIRE NAME="Carp::" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <REQUIRE NAME="Exporter::" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <REQUIRE NAME="File::Spec" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <REQUIRE NAME="File::Temp" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <REQUIRE NAME="IO::Handle" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <REQUIRE NAME="Scalar::Util" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <REQUIRE NAME="strict::" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <REQUIRE NAME="warnings::" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <ARCHITECTURE NAME="x86_64-linux-thread-multi-5.32" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' <CODEBASE HREF="" />' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) ' </IMPLEMENTATION>' >> Capture-Tiny.ppd $(NOECHO) $(ECHO) '</SOFTPKG>' >> Capture-Tiny.ppd # --- MakeMaker pm_to_blib section: pm_to_blib : $(FIRST_MAKEFILE) $(TO_INST_PM) $(NOECHO) $(ABSPERLRUN) -MExtUtils::Install -e 'pm_to_blib({@ARGV}, '\''$(INST_LIB)/auto'\'', q[$(PM_FILTER)], '\''$(PERM_DIR)'\'')' -- \ 'lib/Capture/Tiny.pm' 'blib/lib/Capture/Tiny.pm' $(NOECHO) $(TOUCH) pm_to_blib # --- MakeMaker selfdocument section: # here so even if top_targets is overridden, these will still be defined # gmake will silently still work if any are .PHONY-ed but nmake won't static :: $(NOECHO) $(NOOP) dynamic :: $(NOECHO) $(NOOP) config :: $(NOECHO) $(NOOP) # --- MakeMaker postamble section: # End. META.json 0000644 00000006373 15043642076 0006205 0 ustar 00 { "abstract" : "Capture STDOUT and STDERR from Perl, XS or external programs", "author" : [ "David Golden <dagolden@cpan.org>" ], "dynamic_config" : 1, "generated_by" : "Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010", "license" : [ "apache_2_0" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Capture-Tiny", "no_index" : { "directory" : [ "corpus", "examples", "t", "xt" ], "package" : [ "DB" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.17" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::OSPrereqs" : "0", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::Plugin::ReleaseStatus::FromVersion" : "0", "Dist::Zilla::Plugin::RemovePrereqs" : "0", "Dist::Zilla::PluginBundle::DAGOLDEN" : "0.072", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Pod::Coverage::TrustPod" : "0", "Pod::Wordlist" : "0", "Software::License::Apache_2_0" : "0", "Test::CPAN::Meta" : "0", "Test::MinimumVersion" : "0", "Test::More" : "0", "Test::Perl::Critic" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Portability::Files" : "0", "Test::Spelling" : "0.12", "Test::Version" : "1" } }, "runtime" : { "requires" : { "Carp" : "0", "Exporter" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "Scalar::Util" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "IO::File" : "0", "Test::More" : "0.62", "lib" : "0" } } }, "provides" : { "Capture::Tiny" : { "file" : "lib/Capture/Tiny.pm", "version" : "0.48" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/dagolden/Capture-Tiny/issues" }, "homepage" : "https://github.com/dagolden/Capture-Tiny", "repository" : { "type" : "git", "url" : "https://github.com/dagolden/Capture-Tiny.git", "web" : "https://github.com/dagolden/Capture-Tiny" } }, "version" : "0.48", "x_authority" : "cpan:DAGOLDEN", "x_contributors" : [ "Dagfinn Ilmari Manns\u00e5ker <ilmari@ilmari.org>", "David E. Wheeler <david@justatheory.com>", "fecundf <not.com+github@gmail.com>", "Graham Knop <haarg@haarg.org>", "Peter Rabbitson <ribasushi@cpan.org>" ], "x_generated_by_perl" : "v5.26.1", "x_serialization_backend" : "Cpanel::JSON::XS version 3.0239" } LICENSE 0000644 00000026354 15043642076 0005572 0 ustar 00 This software is Copyright (c) 2009 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. t/25-cap-fork.t 0000644 00000002370 15043642076 0007133 0 ustar 00 # By Yary Hluchan with portions copied from David Golden # Copyright (c) 2015 assigned by Yary Hluchan to David Golden. # All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/next_fd/; use Capture::Tiny 'capture'; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; if ( $no_fork ) { plan skip_all => 'tee() requires fork'; } else { plan 'no_plan'; } my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; my $fd = next_fd; my ($stdout, $stderr, @result) = capture { if (!defined(my $child = fork)) { die "fork() failed" } elsif ($child == 0) { print "Happiness"; print STDERR "Certainty\n"; exit; } else { wait; print ", a parent-ly\n"; } return qw(a b c); }; is ( $stdout, "Happiness, a parent-ly\n", "got stdout"); is ( $stderr, "Certainty\n", "got stderr"); is ( "@result", "a b c" , "got result"); is ( next_fd, $fd, "no file descriptors leaked" ); exit 0; t/06-stdout-closed.t 0000644 00000002003 15043642076 0010212 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; save_std(qw/stdout/); ok( close STDOUT, "closed STDOUT" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); restore_std(qw/stdout/); exit 0; t/19-relayering.t 0000644 00000004722 15043642076 0007600 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/next_fd sig_num/; use Capture::Tiny ':all'; unless ( PerlIO->can('get_layers') ) { plan skip_all => "Requires PerlIO::getlayers"; } plan 'no_plan'; local $ENV{PERL_CAPTURE_TINY_TIMEOUT} = 0; # no timeouts my $builder = Test::More->builder; binmode( $builder->failure_output, ':utf8' ) if $] >= 5.008; my $fd = next_fd; my ( $out, $err, $res, @res, %before, %inner, %outer ); sub _set_layers { my ($fh, $new_layers) = @_; # eliminate pseudo-layers binmode( $fh, ":raw" ) or die "can't binmode $fh"; # strip off real layers until only :unix is left while ( 1 < ( my $layers =()= PerlIO::get_layers( $fh, output => 1 ) ) ) { binmode( $fh, ":pop" ) or die "can't binmode $fh"; } binmode($fh, $new_layers); } sub _get_layers { return ( stdout => [ PerlIO::get_layers( *STDOUT, output => 1 ) ], stderr => [ PerlIO::get_layers( *STDERR, output => 1 ) ], ); } sub _cmp_layers { local $Test::Builder::Level = $Test::Builder::Level + 1; my ($got, $exp, $label) = @_; ($got, $exp) = map { ":" . join(":", @$_) } $got, $exp; is( $got, $exp, $label ); } #--------------------------------------------------------------------------# # relayer should duplicate layers #--------------------------------------------------------------------------# _set_layers( \*STDOUT, ":unix:encoding(UTF-8):encoding(UTF-8):crlf" ); _set_layers( \*STDERR, ":unix:encoding(UTF-8):encoding(UTF-8):crlf" ); %before = _get_layers(); ( $out, $err, @res ) = capture { %inner = _get_layers(); print STDOUT "foo\n"; print STDERR "bar\n"; }; %outer = _get_layers(); _cmp_layers( $inner{$_}, $before{$_}, "$_: layers inside capture match previous" ) for qw/stdout stderr/; _cmp_layers( $outer{$_}, $before{$_}, "$_: layers after capture match previous" ) for qw/stdout stderr/; #--------------------------------------------------------------------------# # finish #--------------------------------------------------------------------------# is( next_fd, $fd, "no file descriptors leaked" ); exit 0; # vim: set ts=4 sts=4 sw=4 et tw=75: t/11-stderr-string.t 0000644 00000002223 15043642076 0010230 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "In memory files require Perl 5.8" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; save_std(qw/stderr/); ok( close STDERR, "closed STDERR" ); ok( open( STDERR, ">", \(my $stderr_buf)), "reopened STDERR to string" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); restore_std(qw/stderr/); exit 0; t/10-stdout-string.t 0000644 00000002224 15043642076 0010247 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "In memory files require Perl 5.8" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; save_std(qw/stdout/); ok( close STDOUT, "closed STDOUT" ); ok( open( STDOUT, ">", \(my $stdout_buf)), "reopened STDOUT to string" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); restore_std(qw/stdout/); exit 0; t/23-all-tied.t 0000644 00000003103 15043642076 0007115 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use TieLC; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "capture needs Perl 5.8 for tied STDOUT" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; binmode($builder->todo_output, ':utf8') if $] >= 5.008; save_std(qw/stdout stderr stdin/); tie *STDOUT, 'TieLC', ">&=STDOUT"; my $out_tie = tied *STDOUT; ok( $out_tie, "STDOUT is tied" ); tie *STDERR, 'TieLC', ">&=STDERR"; my $err_tie = tied *STDERR; ok( $err_tie, "STDERR is tied" ); tie *STDIN, 'TieLC', "<&=STDIN"; my $in_tie = tied *STDIN; ok( $in_tie, "STDIN is tied" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); is( tied *STDOUT, $out_tie, "STDOUT is still tied" ); is( tied *STDERR, $err_tie, "STDERR is still tied" ); is( tied *STDIN, $in_tie, "STDIN is still tied" ); restore_std(qw/stdout stderr stdin/); exit 0; t/20-stdout-badtie.t 0000644 00000002357 15043642076 0010201 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use TieEvil; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "capture needs Perl 5.8 for tied STDOUT" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; binmode($builder->todo_output, ':utf8') if $] >= 5.008; tie *STDOUT, 'TieEvil'; my $orig_tie = tied *STDOUT; ok( $orig_tie, "STDOUT is tied" ); my $fd = next_fd; run_test($_, '', 'skip_utf8') for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_, '', 'skip_utf8') for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); is( tied *STDOUT, $orig_tie, "STDOUT is still tied" ); exit 0; t/01-Capture-Tiny.t 0000644 00000001470 15043642076 0007747 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More 0.62; my @api = qw( capture capture_stdout capture_stderr capture_merged tee tee_stdout tee_stderr tee_merged ); plan tests => 2 + 2 * @api; if ( $] eq '5.008' ) { BAIL_OUT("OS unsupported: Perl 5.8.0 is too buggy for Capture::Tiny"); } require_ok( 'Capture::Tiny' ); can_ok('Capture::Tiny', $_) for @api; ok( eval "package Foo; use Capture::Tiny ':all'; 1", "import ':all' to Foo" ); can_ok('Foo', $_) for @api; exit 0; t/13-stdout-tied.t 0000644 00000002405 15043642076 0007672 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use TieLC; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "capture needs Perl 5.8 for tied STDOUT" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; binmode($builder->todo_output, ':utf8') if $] >= 5.008; save_std(qw/stdout/); tie *STDOUT, 'TieLC', ">&=STDOUT"; my $orig_tie = tied *STDOUT; ok( $orig_tie, "STDOUT is tied" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); is( tied *STDOUT, $orig_tie, "STDOUT is still tied" ); restore_std(qw/stdout/); exit 0; t/24-all-badtied.t 0000644 00000002773 15043642076 0007601 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use TieEvil; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "capture needs Perl 5.8 for tied STDIN" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; binmode($builder->todo_output, ':utf8') if $] >= 5.008; tie *STDIN, 'TieEvil'; my $in_tie = tied *STDIN; ok( $in_tie, "STDIN is tied" ); tie *STDOUT, 'TieEvil'; my $out_tie = tied *STDOUT; ok( $out_tie, "STDIN is tied" ); tie *STDERR, 'TieEvil'; my $err_tie = tied *STDERR; ok( $err_tie, "STDIN is tied" ); my $fd = next_fd; run_test($_, '', 'skip_utf8') for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_, '', 'skip_utf8') for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); is( tied *STDIN, $in_tie, "STDIN is still tied" ); is( tied *STDOUT, $out_tie, "STDOUT is still tied" ); is( tied *STDERR, $err_tie, "STDERR is still tied" ); exit 0; t/17-pass-results.t 0000644 00000005152 15043642076 0010100 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use IO::Handle; use Utils qw/next_fd sig_num/; use Capture::Tiny ':all'; use Config; plan tests => 12; local $ENV{PERL_CAPTURE_TINY_TIMEOUT} = 0; # no timeouts my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; my $fd = next_fd; my ($out, $err, $res, @res); #--------------------------------------------------------------------------# # capture to array #--------------------------------------------------------------------------# ($out, $err, @res) = capture { print STDOUT "foo\n"; print STDERR "bar\n"; return qw/one two three/; }; is( $out, "foo\n", "capture -> STDOUT captured" ); is( $err, "bar\n", "capture -> STDERR captured" ); is_deeply( \@res, [qw/one two three/], "return values -> array" ); #--------------------------------------------------------------------------# # capture to scalar #--------------------------------------------------------------------------# ($out, $err, $res) = capture { print STDOUT "baz\n"; print STDERR "bam\n"; return qw/one two three/; }; is( $out, "baz\n", "capture -> STDOUT captured" ); is( $err, "bam\n", "capture -> STDERR captured" ); is( $res, "one", "return value -> scalar" ); #--------------------------------------------------------------------------# # capture_stdout to array #--------------------------------------------------------------------------# ($out, @res) = capture_stdout { print STDOUT "foo\n"; return qw/one two three/; }; is( $out, "foo\n", "capture_stdout -> STDOUT captured" ); is_deeply( \@res, [qw/one two three/], "return values -> array" ); #--------------------------------------------------------------------------# # capture_merged to array #--------------------------------------------------------------------------# ($out, $res) = capture_merged { print STDOUT "baz\n"; print STDERR "bam\n"; return qw/one two three/; }; like( $out, qr/baz/, "capture_merged -> STDOUT captured" ); like( $out, qr/bam/, "capture_merged -> STDERR captured" ); is( $res, "one", "return value -> scalar" ); #--------------------------------------------------------------------------# # finish #--------------------------------------------------------------------------# is( next_fd, $fd, "no file descriptors leaked" ); exit 0; t/00-report-prereqs.dd 0000644 00000005742 15043642076 0010546 0 ustar 00 do { my $x = { 'configure' => { 'requires' => { 'ExtUtils::MakeMaker' => '6.17' } }, 'develop' => { 'requires' => { 'Dist::Zilla' => '5', 'Dist::Zilla::Plugin::OSPrereqs' => '0', 'Dist::Zilla::Plugin::Prereqs' => '0', 'Dist::Zilla::Plugin::ReleaseStatus::FromVersion' => '0', 'Dist::Zilla::Plugin::RemovePrereqs' => '0', 'Dist::Zilla::PluginBundle::DAGOLDEN' => '0.072', 'File::Spec' => '0', 'File::Temp' => '0', 'IO::Handle' => '0', 'IPC::Open3' => '0', 'Pod::Coverage::TrustPod' => '0', 'Pod::Wordlist' => '0', 'Software::License::Apache_2_0' => '0', 'Test::CPAN::Meta' => '0', 'Test::MinimumVersion' => '0', 'Test::More' => '0', 'Test::Perl::Critic' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.08', 'Test::Portability::Files' => '0', 'Test::Spelling' => '0.12', 'Test::Version' => '1' } }, 'runtime' => { 'requires' => { 'Carp' => '0', 'Exporter' => '0', 'File::Spec' => '0', 'File::Temp' => '0', 'IO::Handle' => '0', 'Scalar::Util' => '0', 'perl' => '5.006', 'strict' => '0', 'warnings' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'ExtUtils::MakeMaker' => '0', 'File::Spec' => '0', 'IO::File' => '0', 'Test::More' => '0.62', 'lib' => '0' } } }; $x; } t/00-report-prereqs.t 0000644 00000013426 15043642076 0010420 0 ustar 00 #!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.027 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if $mod eq 'perl'; next if grep { $_ eq $mod } @exclude; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass; # vim: ts=4 sts=4 sw=4 et: t/12-stdin-string.t 0000644 00000002574 15043642076 0010060 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "In memory files require Perl 5.8" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; #--------------------------------------------------------------------------# # pre-load PerlIO::scalar to avoid it opening on FD 0; c.f. # http://www.nntp.perl.org/group/perl.perl5.porters/2008/07/msg138898.html require PerlIO::scalar; save_std(qw/stdin/); ok( close STDIN, "closed STDIN" ); ok( open( STDIN, "<", \(my $stdin_buf)), "reopened STDIN to string" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); restore_std(qw/stdin/); exit 0; t/18-custom-capture.t 0000644 00000011025 15043642076 0010403 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use IO::Handle; use IO::File; use File::Temp qw/tmpnam/; use Utils qw/next_fd sig_num/; use Capture::Tiny ':all'; use Config; plan tests => 19; local $ENV{PERL_CAPTURE_TINY_TIMEOUT} = 0; # no timeouts my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; my $fd = next_fd; my ($out, $err, $res, @res); #--------------------------------------------------------------------------# # capture to custom IO::File #--------------------------------------------------------------------------# my $temp_out = tmpnam(); my $temp_err = tmpnam(); ok( !-e $temp_out, "Temp out '$temp_out' doesn't exist" ); ok( !-e $temp_err, "Temp out '$temp_err' doesn't exist" ); my $out_fh = IO::File->new($temp_out, "w+"); my $err_fh = IO::File->new($temp_err, "w+"); capture { print STDOUT "foo\n"; print STDERR "bar\n"; } stdout => $out_fh, stderr => $err_fh; $out_fh->close; $err_fh->close; is( scalar do {local (@ARGV,$/) = $temp_out; <>} , "foo\n", "captured STDOUT to custom handle (IO::File)" ); is( scalar do {local (@ARGV,$/) = $temp_err; <>} , "bar\n", "captured STDERR to custom handle (IO::File)" ); unlink $_ for $temp_out, $temp_err; #--------------------------------------------------------------------------# # capture to GLOB handle #--------------------------------------------------------------------------# $temp_out = tmpnam(); $temp_err = tmpnam(); ok( !-e $temp_out, "Temp out '$temp_out' doesn't exist" ); ok( !-e $temp_err, "Temp out '$temp_err' doesn't exist" ); open $out_fh, "+>", $temp_out; open $err_fh, "+>", $temp_err; capture { print STDOUT "foo\n"; print STDERR "bar\n"; } stdout => $out_fh, stderr => $err_fh; $out_fh->close; $err_fh->close; is( scalar do {local (@ARGV,$/) = $temp_out; <>} , "foo\n", "captured STDOUT to custom handle (GLOB)" ); is( scalar do {local (@ARGV,$/) = $temp_err; <>} , "bar\n", "captured STDERR to custom handle (GLOB)" ); unlink $_ for $temp_out, $temp_err; #--------------------------------------------------------------------------# # append to custom IO::File #--------------------------------------------------------------------------# $temp_out = tmpnam(); $temp_err = tmpnam(); ok( !-e $temp_out, "Temp out '$temp_out' doesn't exist" ); ok( !-e $temp_err, "Temp out '$temp_err' doesn't exist" ); $out_fh = IO::File->new($temp_out, "w+"); $err_fh = IO::File->new($temp_err, "w+"); $out_fh->autoflush(1); $err_fh->autoflush(1); print $out_fh "Shouldn't see this in capture\n"; print $err_fh "Shouldn't see this in capture\n"; my ($got_out, $got_err) = capture { print STDOUT "foo\n"; print STDERR "bar\n"; } stdout => $out_fh, stderr => $err_fh; $out_fh->close; $err_fh->close; is( $got_out, "foo\n", "captured appended STDOUT to custom handle" ); is( $got_err, "bar\n", "captured appended STDERR to custom handle" ); unlink $_ for $temp_out, $temp_err; #--------------------------------------------------------------------------# # repeated append to custom IO::File with no output #--------------------------------------------------------------------------# $temp_out = tmpnam(); $temp_err = tmpnam(); ok( !-e $temp_out, "Temp out '$temp_out' doesn't exist" ); ok( !-e $temp_err, "Temp out '$temp_err' doesn't exist" ); $out_fh = IO::File->new($temp_out, "a+"); $err_fh = IO::File->new($temp_err, "a+"); ($got_out, $got_err) = capture { my $i = 0; $i++ for 1 .. 10; # no output, just busywork } stdout => $out_fh, stderr => $err_fh; is( $got_out, "", "Try 1: captured empty appended STDOUT to custom handle" ); is( $got_err, "", "Try 1: captured empty appended STDERR to custom handle" ); ($got_out, $got_err) = capture { my $i = 0; $i++ for 1 .. 10; # no output, just busywork } stdout => $out_fh, stderr => $err_fh; is( $got_out, "", "Try 2: captured empty appended STDOUT to custom handle" ); is( $got_err, "", "Try 2: captured empty appended STDERR to custom handle" ); unlink $_ for $temp_out, $temp_err; #--------------------------------------------------------------------------# # finish #--------------------------------------------------------------------------# close ARGV; # opened by reading from <> is( next_fd, $fd, "no file descriptors leaked" ); exit 0; t/08-stdin-closed.t 0000644 00000003101 15043642076 0010013 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; # XXX work around a bug in perl; this needs to be called early-ish # to avoid some sort of filehandle leak when combined with Capture::Tiny my $qm = quotemeta("\x{263a}"); save_std(qw/stdin/); ok( close STDIN, "closed STDIN" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { # prior to 5.12, PERL_UNICODE=D causes problems when STDIN is closed # before capturing. No idea why. Documented as a known issue. if ( $] lt '5.012' && ${^UNICODE} & 24 ) { diag 'Skipping tee() tests because PERL_UNICODE=D not supported'; } else { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } } if ( $] lt '5.012' && ${^UNICODE} & 24 ) { diag 'Skipping leak test because PERL_UNICODE=D not supported'; } else { is( next_fd, $fd, "no file descriptors leaked" ); } restore_std(qw/stdin/); exit 0; t/16-catch-errors.t 0000644 00000002273 15043642076 0010027 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/next_fd sig_num/; use Capture::Tiny qw/capture tee/; use Config; plan tests => 5; local $ENV{PERL_CAPTURE_TINY_TIMEOUT} = 0; # no timeouts my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; my $fd = next_fd; $@ = "initial error"; my ($out, $err) = capture { print "foo\n" }; is( $@, 'initial error', "Initial \$\@ not lost during capture" ); ($out, $err) = capture { eval { tee { local $|=1; print STDOUT "foo\n"; print STDERR "bar\n"; die "Fatal error in capture\n"; } }; }; my $error = $@; is( $error, "Fatal error in capture\n", "\$\@ preserved after capture" ); is( $out, "foo\n", "STDOUT still captured" ); is( $err, "bar\n", "STDOUT still captured" ); is( next_fd, $fd, "no file descriptors leaked" ); exit 0; t/09-preserve-exit-code.t 0000644 00000001341 15043642076 0011142 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/next_fd sig_num/; use Capture::Tiny qw/capture/; use Config; plan tests => 2; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; my $fd = next_fd; capture { $? = 42; }; is( $?, 42, "\$\? preserved after capture ends" ); is( next_fd, $fd, "no file descriptors leaked" ); exit 0; t/21-stderr-badtie.t 0000644 00000002357 15043642076 0010163 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use TieEvil; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "capture needs Perl 5.8 for tied STDERR" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; binmode($builder->todo_output, ':utf8') if $] >= 5.008; tie *STDERR, 'TieEvil'; my $orig_tie = tied *STDERR; ok( $orig_tie, "STDERR is tied" ); my $fd = next_fd; run_test($_, '', 'skip_utf8') for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_, '', 'skip_utf8') for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); is( tied *STDERR, $orig_tie, "STDERR is still tied" ); exit 0; t/14-stderr-tied.t 0000644 00000002406 15043642076 0007655 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use TieLC; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "capture needs Perl 5.8 for tied STDERR" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; binmode($builder->todo_output, ':utf8') if $] >= 5.008; save_std(qw/stderr/); tie *STDERR, 'TieLC', ">&=STDERR"; my $orig_tie = tied *STDERR; ok( $orig_tie, "STDERR is tied" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); is( tied *STDERR, $orig_tie, "STDERR is still tied" ); restore_std(qw/stderr/); exit 0; t/15-stdin-tied.t 0000644 00000002420 15043642076 0007470 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use TieLC; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "capture needs Perl 5.8 for tied STDERR" if $] < 5.008; #plan skip_all => "not supported on Windows yet" # if $^O eq 'MSWin32'; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; save_std(qw/stdin/); tie *STDIN, 'TieLC', "<&=STDIN"; my $orig_tie = tied *STDIN; ok( $orig_tie, "STDIN is tied" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); is( tied *STDIN, $orig_tie, "STDIN is still tied" ); restore_std(qw/stdin/); exit 0; t/03-tee.t 0000644 00000001564 15043642076 0006206 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/next_fd/; use Cases qw/run_test/; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; if ( $no_fork ) { plan skip_all => 'tee() requires fork'; } else { plan 'no_plan'; } my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; my $fd = next_fd; run_test('tee'); run_test('tee_scalar'); run_test('tee_stdout'); run_test('tee_stderr'); run_test('tee_merged'); is( next_fd, $fd, "no file descriptors leaked" ); exit 0; t/07-stderr-closed.t 0000644 00000002002 15043642076 0010173 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; save_std(qw/stderr/); ok( close STDERR, "closed STDERR" ); my $fd = next_fd; run_test($_) for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_) for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); restore_std(qw/stderr/); exit 0; t/02-capture.t 0000644 00000001376 15043642076 0007074 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/next_fd/; use Cases qw/run_test/; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; my $fd = next_fd; run_test('capture'); run_test('capture_scalar'); run_test('capture_stdout'); run_test('capture_stderr'); run_test('capture_merged'); is( next_fd, $fd, "no file descriptors leaked" ); exit 0; t/22-stdin-badtie.t 0000644 00000002351 15043642076 0007774 0 ustar 00 # Copyright (c) 2009 by David Golden. All rights reserved. # Licensed under Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License was distributed with this file or you may obtain a # copy of the License from http://www.apache.org/licenses/LICENSE-2.0 use strict; use warnings; use Test::More; use lib 't/lib'; use Utils qw/save_std restore_std next_fd/; use Cases qw/run_test/; use TieEvil; use Config; my $no_fork = $^O ne 'MSWin32' && ! $Config{d_fork}; plan skip_all => "capture needs Perl 5.8 for tied STDIN" if $] < 5.008; plan 'no_plan'; my $builder = Test::More->builder; binmode($builder->failure_output, ':utf8') if $] >= 5.008; binmode($builder->todo_output, ':utf8') if $] >= 5.008; tie *STDIN, 'TieEvil'; my $orig_tie = tied *STDIN; ok( $orig_tie, "STDIN is tied" ); my $fd = next_fd; run_test($_, '', 'skip_utf8') for qw( capture capture_scalar capture_stdout capture_stderr capture_merged ); if ( ! $no_fork ) { run_test($_, '', 'skip_utf8') for qw( tee tee_scalar tee_stdout tee_stderr tee_merged ); } is( next_fd, $fd, "no file descriptors leaked" ); is( tied *STDIN, $orig_tie, "STDIN is still tied" ); exit 0; t/lib/Utils.pm 0000644 00000002175 15043642076 0007227 0 ustar 00 package Utils; use strict; use warnings; use File::Spec; use Config; require Exporter; our @ISA = 'Exporter'; our @EXPORT = qw/save_std restore_std next_fd sig_num/; sub _open { open $_[0], $_[1] or die "Error from open( " . join(q{, }, @_) . "): $!"; } my @saved; sub save_std { for my $h ( @_ ) { my $fh; _open $fh, ($h eq 'stdin' ? "<&" : ">&") . uc $h; push @saved, $fh; } } sub restore_std { for my $h ( @_ ) { no strict 'refs'; my $fh = shift @saved; _open \*{uc $h}, ($h eq 'stdin' ? "<&" : ">&") . fileno( $fh ); close $fh; } } sub next_fd { no warnings 'io'; open my $fh, ">", File::Spec->devnull; my $fileno = fileno $fh; close $fh; return $fileno; } #--------------------------------------------------------------------------# my %sig_num; my @sig_name; unless($Config{sig_name} && $Config{sig_num}) { die "No sigs?"; } else { my @names = split ' ', $Config{sig_name}; @sig_num{@names} = split ' ', $Config{sig_num}; foreach (@names) { $sig_name[$sig_num{$_}] ||= $_; } } sub sig_num { my $name = shift; return exists $sig_num{$name} ? $sig_num{$name} : ''; } 1; t/lib/TieLC.pm 0000644 00000001460 15043642076 0007063 0 ustar 00 package TieLC; sub TIEHANDLE { my $class = shift; my $fh = \do { local *HANDLE}; bless $fh,$class; $fh->OPEN(@_) if (@_); $fh->BINMODE(':utf8'); return $fh; } sub EOF { eof($_[0]) } sub TELL { tell($_[0]) } sub FILENO { fileno($_[0]) } sub SEEK { seek($_[0],$_[1],$_[2]) } sub CLOSE { close($_[0]) } sub BINMODE { binmode($_[0],$_[1]) } sub OPEN { $_[0]->CLOSE if defined($_[0]->FILENO); @_ == 2 ? open($_[0], $_[1]) : open($_[0], $_[1], $_[2]); } sub READ { read($_[0],$_[1],$_[2]) } sub READLINE { "hello world\n" } sub GETC { getc($_[0]) } sub WRITE { my $fh = $_[0]; print $fh substr($_[1],0,$_[2]) } sub PRINT { my ($self, @what) = @_; my $buf = lc join('', @what); $self->WRITE($buf, length($buf), 0); } sub UNTIE { 1 }; # suppress warnings about references 1; t/lib/Cases.pm 0000644 00000021601 15043642076 0007160 0 ustar 00 package Cases; use strict; use warnings; use Test::More; use Capture::Tiny ':all'; require Exporter; our @ISA = 'Exporter'; our @EXPORT_OK = qw( run_test ); my $locale_ok = eval { my $err = capture_stderr { system($^X, '-we', 1) }; $err !~ /setting locale failed/i; }; my $have_diff = eval { require Test::Differences; Test::Differences->import; $Test::Differences::VERSION < 0.60; # 0.60+ is causing strange failures }; sub _is_or_diff { my ($g,$e,$l) = @_; if ( $have_diff ) { eq_or_diff( $g, $e, $l ); } else { is( $g, $e, $l ); } } sub _binmode { my $text = shift; return $text eq 'unicode' ? 'binmode(STDOUT,q{:utf8}); binmode(STDERR,q{:utf8});' : ''; } sub _set_utf8 { my $t = shift; return unless $t eq 'unicode'; my %seen; my @orig_layers = ( [ grep {$_ ne 'unix' and $_ ne 'perlio' and $seen{stdout}{$_}++} PerlIO::get_layers(\*STDOUT) ], [ grep {$_ ne 'unix' and $_ ne 'perlio' and $seen{stderr}{$_}++} PerlIO::get_layers(\*STDERR) ], ); binmode(STDOUT, ":utf8") if fileno(STDOUT); binmode(STDERR, ":utf8") if fileno(STDERR); return @orig_layers; } sub _restore_layers { my ($t, @orig_layers) = @_; return unless $t eq 'unicode'; binmode(STDOUT, join( ":", "", "raw", @{$orig_layers[0]})) if fileno(STDOUT); binmode(STDERR, join( ":", "", "raw", @{$orig_layers[1]})) if fileno(STDERR); } #--------------------------------------------------------------------------# my %texts = ( short => 'Hello World', multiline => 'First line\nSecond line\n', ( $] lt "5.008" ? () : ( unicode => 'Hi! \x{263a}\n') ), ); #--------------------------------------------------------------------------# # fcn($perl_code_string) => execute the perl in current process or subprocess #--------------------------------------------------------------------------# my %methods = ( perl => sub { eval $_[0] }, sys => sub { system($^X, '-e', $_[0]) }, ); #--------------------------------------------------------------------------# my %channels = ( stdout => { output => sub { _binmode($_[0]) . "print STDOUT qq{STDOUT:$texts{$_[0]}}" }, expect => sub { eval "qq{STDOUT:$texts{$_[0]}}", "" }, }, stderr => { output => sub { _binmode($_[0]) . "print STDERR qq{STDERR:$texts{$_[0]}}" }, expect => sub { "", eval "qq{STDERR:$texts{$_[0]}}" }, }, both => { output => sub { _binmode($_[0]) . "print STDOUT qq{STDOUT:$texts{$_[0]}}; print STDERR qq{STDERR:$texts{$_[0]}}" }, expect => sub { eval "qq{STDOUT:$texts{$_[0]}}", eval "qq{STDERR:$texts{$_[0]}}" }, }, empty => { output => sub { _binmode($_[0]) . "print STDOUT qq{}; print STDERR qq{}" }, expect => sub { "", "" }, }, nooutput=> { output => sub { _binmode($_[0]) }, expect => sub { "", "" }, }, ); #--------------------------------------------------------------------------# my %tests = ( capture => { cnt => 2, test => sub { my ($m, $c, $t, $l) = @_; my ($got_out, $got_err) = capture { $methods{$m}->( $channels{$c}{output}->($t) ); }; my @expected = $channels{$c}{expect}->($t); _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" ); _is_or_diff( $got_err, $expected[1], "$l|$m|$c|$t - got STDERR" ); }, }, capture_scalar => { cnt => 1, test => sub { my ($m, $c, $t, $l) = @_; my $got_out = capture { $methods{$m}->( $channels{$c}{output}->($t) ); }; my @expected = $channels{$c}{expect}->($t); _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" ); }, }, capture_stdout => { cnt => 3, test => sub { my ($m, $c, $t, $l) = @_; my ($inner_out, $inner_err); my ($outer_out, $outer_err) = capture { $inner_out = capture_stdout { $methods{$m}->( $channels{$c}{output}->($t) ); }; }; my @expected = $channels{$c}{expect}->($t); _is_or_diff( $inner_out, $expected[0], "$l|$m|$c|$t - inner STDOUT" ); _is_or_diff( $outer_out, "", "$l|$m|$c|$t - outer STDOUT" ); _is_or_diff( $outer_err, $expected[1], "$l|$m|$c|$t - outer STDERR" ); }, }, capture_stderr => { cnt => 3, test => sub { my ($m, $c, $t, $l) = @_; my ($inner_out, $inner_err); my ($outer_out, $outer_err) = capture { $inner_err = capture_stderr { $methods{$m}->( $channels{$c}{output}->($t) ); }; }; my @expected = $channels{$c}{expect}->($t); _is_or_diff( $inner_err, $expected[1], "$l|$m|$c|$t - inner STDERR" ); _is_or_diff( $outer_out, $expected[0], "$l|$m|$c|$t - outer STDOUT" ); _is_or_diff( $outer_err, "", "$l|$m|$c|$t - outer STDERR" ); }, }, capture_merged => { cnt => 2, test => sub { my ($m, $c, $t, $l) = @_; my $got_out = capture_merged { $methods{$m}->( $channels{$c}{output}->($t) ); }; my @expected = $channels{$c}{expect}->($t); like( $got_out, qr/\Q$expected[0]\E/, "$l|$m|$c|$t - got STDOUT" ); like( $got_out, qr/\Q$expected[1]\E/, "$l|$m|$c|$t - got STDERR" ); }, }, tee => { cnt => 4, test => sub { my ($m, $c, $t, $l) = @_; my ($got_out, $got_err); my ($tee_out, $tee_err) = capture { ($got_out, $got_err) = tee { $methods{$m}->( $channels{$c}{output}->($t) ); }; }; my @expected = $channels{$c}{expect}->($t); _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" ); _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - tee STDOUT" ); _is_or_diff( $got_err, $expected[1], "$l|$m|$c|$t - got STDERR" ); _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - tee STDERR" ); } }, tee_scalar => { cnt => 3, test => sub { my ($m, $c, $t, $l) = @_; my ($got_out, $got_err); my ($tee_out, $tee_err) = capture { $got_out = tee { $methods{$m}->( $channels{$c}{output}->($t) ); }; }; my @expected = $channels{$c}{expect}->($t); _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" ); _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - tee STDOUT" ); _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - tee STDERR" ); } }, tee_stdout => { cnt => 3, test => sub { my ($m, $c, $t, $l) = @_; my ($inner_out, $inner_err); my ($tee_out, $tee_err) = capture { $inner_out = tee_stdout { $methods{$m}->( $channels{$c}{output}->($t) ); }; }; my @expected = $channels{$c}{expect}->($t); _is_or_diff( $inner_out, $expected[0], "$l|$m|$c|$t - inner STDOUT" ); _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - teed STDOUT" ); _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - unmodified STDERR" ); } }, tee_stderr => { cnt => 3, test => sub { my ($m, $c, $t, $l) = @_; my ($inner_out, $inner_err); my ($tee_out, $tee_err) = capture { $inner_err = tee_stderr { $methods{$m}->( $channels{$c}{output}->($t) ); }; }; my @expected = $channels{$c}{expect}->($t); _is_or_diff( $inner_err, $expected[1], "$l|$m|$c|$t - inner STDOUT" ); _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - unmodified STDOUT" ); _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - teed STDERR" ); } }, tee_merged => { cnt => 5, test => sub { my ($m, $c, $t, $l) = @_; my ($got_out, $got_err); my ($tee_out, $tee_err) = capture { $got_out = tee_merged { $methods{$m}->( $channels{$c}{output}->($t) ); }; }; my @expected = $channels{$c}{expect}->($t); like( $got_out, qr/\Q$expected[0]\E/, "$l|$m|$c|$t - got STDOUT" ); like( $got_out, qr/\Q$expected[1]\E/, "$l|$m|$c|$t - got STDERR" ); like( $tee_out, qr/\Q$expected[0]\E/, "$l|$m|$c|$t - tee STDOUT (STDOUT)" ); like( $tee_out, qr/\Q$expected[1]\E/, "$l|$m|$c|$t - tee STDOUT (STDERR)" ); _is_or_diff( $tee_err, '', "$l|$m|$c|$t - tee STDERR" ); } }, ); #--------------------------------------------------------------------------# # What I want to be able to do: # # test_it( # input => 'short', # channels => 'both', # method => 'perl' # ) sub run_test { my $test_type = shift or return; my $todo = shift || ''; my $skip_utf8 = shift || ''; local $ENV{PERL_CAPTURE_TINY_TIMEOUT} = 0; # don't timeout during testing for my $m ( keys %methods ) { if ( ($m eq 'sys' || substr($test_type,0,3) eq 'tee' ) && ! $locale_ok ) { SKIP: { skip "Perl could not initialize locale", 1 }; next; } for my $c ( keys %channels ) { for my $t ( keys %texts ) { next if $t eq 'unicode' && $skip_utf8; my @orig_layers = _set_utf8($t); local $TODO = "not supported on all platforms" if $t eq $todo; $tests{$test_type}{test}->($m, $c, $t, $test_type); _restore_layers($t, @orig_layers); } } } } 1; t/lib/TieEvil.pm 0000644 00000001332 15043642076 0007462 0 ustar 00 package TieEvil; # FCGI tied with a scalar ref object, which breaks when you # call open on it. Emulate that to test the workaround: use Carp (); sub TIEHANDLE { my $class = shift; my $fh = \(my $scalar); # this is evil and broken return bless $fh,$class; } sub EOF { 0 } sub TELL { length ${$_[0]} } sub FILENO { -1 } sub SEEK { 1 } sub CLOSE { 1 } sub BINMODE { 1 } sub OPEN { Carp::confess "unimplemented" } sub READ { $_[1] = substr(${$_[0]},$_[3],$_[2]) } sub READLINE { "hello world\n" } sub GETC { substr(${$_[0]},0,1) } sub PRINT { my ($self, @what) = @_; my $new = join($\, @what); $$self .= $new; return length $new; } sub UNTIE { 1 }; # suppress warnings about references 1; perlcritic.rc 0000644 00000001166 15043642076 0007245 0 ustar 00 severity = 5 verbose = 8 [Variables::ProhibitPunctuationVars] allow = $@ $! [TestingAndDebugging::ProhibitNoStrict] allow = refs [Variables::ProhibitEvilVariables] variables = $DB::single # Turn these off [-BuiltinFunctions::ProhibitStringyEval] [-ControlStructures::ProhibitPostfixControls] [-ControlStructures::ProhibitUnlessBlocks] [-Documentation::RequirePodSections] [-InputOutput::ProhibitInteractiveTest] [-References::ProhibitDoubleSigils] [-RegularExpressions::RequireExtendedFormatting] [-InputOutput::ProhibitTwoArgOpen] [-Modules::ProhibitEvilModules] # Turn this on [Lax::ProhibitStringyEval::ExceptForRequire] META.yml 0000644 00000002537 15043642076 0006033 0 ustar 00 --- abstract: 'Capture STDOUT and STDERR from Perl, XS or external programs' author: - 'David Golden <dagolden@cpan.org>' build_requires: ExtUtils::MakeMaker: '0' File::Spec: '0' IO::File: '0' Test::More: '0.62' lib: '0' configure_requires: ExtUtils::MakeMaker: '6.17' dynamic_config: 1 generated_by: 'Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010' license: apache meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Capture-Tiny no_index: directory: - corpus - examples - t - xt package: - DB provides: Capture::Tiny: file: lib/Capture/Tiny.pm version: '0.48' requires: Carp: '0' Exporter: '0' File::Spec: '0' File::Temp: '0' IO::Handle: '0' Scalar::Util: '0' perl: '5.006' strict: '0' warnings: '0' resources: bugtracker: https://github.com/dagolden/Capture-Tiny/issues homepage: https://github.com/dagolden/Capture-Tiny repository: https://github.com/dagolden/Capture-Tiny.git version: '0.48' x_authority: cpan:DAGOLDEN x_contributors: - 'Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>' - 'David E. Wheeler <david@justatheory.com>' - 'fecundf <not.com+github@gmail.com>' - 'Graham Knop <haarg@haarg.org>' - 'Peter Rabbitson <ribasushi@cpan.org>' x_generated_by_perl: v5.26.1 x_serialization_backend: 'YAML::Tiny version 1.70' dist.ini 0000644 00000001067 15043642076 0006223 0 ustar 00 name = Capture-Tiny author = David Golden <dagolden@cpan.org> license = Apache_2_0 copyright_holder = David Golden copyright_year = 2009 [@DAGOLDEN] :version = 0.072 stopwords = UTF stopwords = seekable stopwords = prototyped stopwords = resending stopwords = undiagnosed [ReleaseStatus::FromVersion] testing = second_decimal_odd [OSPrereqs / MSWin32] Win32API::File = 0 [RemovePrereqs] remove = PerlIO remove = PerlIO::scalar remove = Test::Differences ; tests optionally require 5.008 remove = perl [Prereqs] perl = 5.006 Makefile.PL 0000644 00000003154 15043642076 0006530 0 ustar 00 # This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.012. use strict; use warnings; use 5.006; use ExtUtils::MakeMaker 6.17; my %WriteMakefileArgs = ( "ABSTRACT" => "Capture STDOUT and STDERR from Perl, XS or external programs", "AUTHOR" => "David Golden <dagolden\@cpan.org>", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => "6.17" }, "DISTNAME" => "Capture-Tiny", "LICENSE" => "apache", "MIN_PERL_VERSION" => "5.006", "NAME" => "Capture::Tiny", "PREREQ_PM" => { "Carp" => 0, "Exporter" => 0, "File::Spec" => 0, "File::Temp" => 0, "IO::Handle" => 0, "Scalar::Util" => 0, "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "IO::File" => 0, "Test::More" => "0.62", "lib" => 0 }, "VERSION" => "0.48", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Carp" => 0, "Exporter" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "File::Temp" => 0, "IO::File" => 0, "IO::Handle" => 0, "Scalar::Util" => 0, "Test::More" => "0.62", "lib" => 0, "strict" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; if ( $^O eq 'MSWin32' ) { $WriteMakefileArgs{PREREQ_PM}{'Win32API::File'} = $FallbackPrereqs{'Win32API::File'} = '0'; } WriteMakefile(%WriteMakefileArgs); blib/man3/Capture::Tiny.3pm 0000644 00000042602 15043642076 0011401 0 ustar 00 .\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Capture::Tiny 3" .TH Capture::Tiny 3 "2018-04-22" "perl v5.32.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Capture::Tiny \- Capture STDOUT and STDERR from Perl, XS or external programs .SH "VERSION" .IX Header "VERSION" version 0.48 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Capture::Tiny \*(Aq:all\*(Aq; \& \& # capture from external command \& \& ($stdout, $stderr, $exit) = capture { \& system( $cmd, @args ); \& }; \& \& # capture from arbitrary code (Perl or external) \& \& ($stdout, $stderr, @result) = capture { \& # your code here \& }; \& \& # capture partial or merged output \& \& $stdout = capture_stdout { ... }; \& $stderr = capture_stderr { ... }; \& $merged = capture_merged { ... }; \& \& # tee output \& \& ($stdout, $stderr) = tee { \& # your code here \& }; \& \& $stdout = tee_stdout { ... }; \& $stderr = tee_stderr { ... }; \& $merged = tee_merged { ... }; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Capture::Tiny provides a simple, portable way to capture almost anything sent to \s-1STDOUT\s0 or \s-1STDERR,\s0 regardless of whether it comes from Perl, from \s-1XS\s0 code or from an external program. Optionally, output can be teed so that it is captured while being passed through to the original filehandles. Yes, it even works on Windows (usually). Stop guessing which of a dozen capturing modules to use in any particular situation and just use this one. .SH "USAGE" .IX Header "USAGE" The following functions are available. None are exported by default. .SS "capture" .IX Subsection "capture" .Vb 2 \& ($stdout, $stderr, @result) = capture \e&code; \& $stdout = capture \e&code; .Ve .PP The \f(CW\*(C`capture\*(C'\fR function takes a code reference and returns what is sent to \&\s-1STDOUT\s0 and \s-1STDERR\s0 as well as any return values from the code reference. In scalar context, it returns only \s-1STDOUT.\s0 If no output was received for a filehandle, it returns an empty string for that filehandle. Regardless of calling context, all output is captured \*(-- nothing is passed to the existing filehandles. .PP It is prototyped to take a subroutine reference as an argument. Thus, it can be called in block form: .PP .Vb 3 \& ($stdout, $stderr) = capture { \& # your code here ... \& }; .Ve .PP Note that the coderef is evaluated in list context. If you wish to force scalar context on the return value, you must use the \f(CW\*(C`scalar\*(C'\fR keyword. .PP .Vb 4 \& ($stdout, $stderr, $count) = capture { \& my @list = qw/one two three/; \& return scalar @list; # $count will be 3 \& }; .Ve .PP Also note that within the coderef, the \f(CW@_\fR variable will be empty. So don't use arguments from a surrounding subroutine without copying them to an array first: .PP .Vb 4 \& sub wont_work { \& my ($stdout, $stderr) = capture { do_stuff( @_ ) }; # WRONG \& ... \& } \& \& sub will_work { \& my @args = @_; \& my ($stdout, $stderr) = capture { do_stuff( @args ) }; # RIGHT \& ... \& } .Ve .PP Captures are normally done to an anonymous temporary filehandle. To capture via a named file (e.g. to externally monitor a long-running capture), provide custom filehandles as a trailing list of option pairs: .PP .Vb 3 \& my $out_fh = IO::File\->new("out.txt", "w+"); \& my $err_fh = IO::File\->new("out.txt", "w+"); \& capture { ... } stdout => $out_fh, stderr => $err_fh; .Ve .PP The filehandles must be read/write and seekable. Modifying the files or filehandles during a capture operation will give unpredictable results. Existing \s-1IO\s0 layers on them may be changed by the capture. .PP When called in void context, \f(CW\*(C`capture\*(C'\fR saves memory and time by not reading back from the capture handles. .SS "capture_stdout" .IX Subsection "capture_stdout" .Vb 2 \& ($stdout, @result) = capture_stdout \e&code; \& $stdout = capture_stdout \e&code; .Ve .PP The \f(CW\*(C`capture_stdout\*(C'\fR function works just like \f(CW\*(C`capture\*(C'\fR except only \&\s-1STDOUT\s0 is captured. \s-1STDERR\s0 is not captured. .SS "capture_stderr" .IX Subsection "capture_stderr" .Vb 2 \& ($stderr, @result) = capture_stderr \e&code; \& $stderr = capture_stderr \e&code; .Ve .PP The \f(CW\*(C`capture_stderr\*(C'\fR function works just like \f(CW\*(C`capture\*(C'\fR except only \&\s-1STDERR\s0 is captured. \s-1STDOUT\s0 is not captured. .SS "capture_merged" .IX Subsection "capture_merged" .Vb 2 \& ($merged, @result) = capture_merged \e&code; \& $merged = capture_merged \e&code; .Ve .PP The \f(CW\*(C`capture_merged\*(C'\fR function works just like \f(CW\*(C`capture\*(C'\fR except \s-1STDOUT\s0 and \&\s-1STDERR\s0 are merged. (Technically, \s-1STDERR\s0 is redirected to the same capturing handle as \s-1STDOUT\s0 before executing the function.) .PP Caution: \s-1STDOUT\s0 and \s-1STDERR\s0 output in the merged result are not guaranteed to be properly ordered due to buffering. .SS "tee" .IX Subsection "tee" .Vb 2 \& ($stdout, $stderr, @result) = tee \e&code; \& $stdout = tee \e&code; .Ve .PP The \f(CW\*(C`tee\*(C'\fR function works just like \f(CW\*(C`capture\*(C'\fR, except that output is captured as well as passed on to the original \s-1STDOUT\s0 and \s-1STDERR.\s0 .PP When called in void context, \f(CW\*(C`tee\*(C'\fR saves memory and time by not reading back from the capture handles, except when the original \s-1STDOUT OR STDERR\s0 were tied or opened to a scalar handle. .SS "tee_stdout" .IX Subsection "tee_stdout" .Vb 2 \& ($stdout, @result) = tee_stdout \e&code; \& $stdout = tee_stdout \e&code; .Ve .PP The \f(CW\*(C`tee_stdout\*(C'\fR function works just like \f(CW\*(C`tee\*(C'\fR except only \&\s-1STDOUT\s0 is teed. \s-1STDERR\s0 is not teed (output goes to \s-1STDERR\s0 as usual). .SS "tee_stderr" .IX Subsection "tee_stderr" .Vb 2 \& ($stderr, @result) = tee_stderr \e&code; \& $stderr = tee_stderr \e&code; .Ve .PP The \f(CW\*(C`tee_stderr\*(C'\fR function works just like \f(CW\*(C`tee\*(C'\fR except only \&\s-1STDERR\s0 is teed. \s-1STDOUT\s0 is not teed (output goes to \s-1STDOUT\s0 as usual). .SS "tee_merged" .IX Subsection "tee_merged" .Vb 2 \& ($merged, @result) = tee_merged \e&code; \& $merged = tee_merged \e&code; .Ve .PP The \f(CW\*(C`tee_merged\*(C'\fR function works just like \f(CW\*(C`capture_merged\*(C'\fR except that output is captured as well as passed on to \s-1STDOUT.\s0 .PP Caution: \s-1STDOUT\s0 and \s-1STDERR\s0 output in the merged result are not guaranteed to be properly ordered due to buffering. .SH "LIMITATIONS" .IX Header "LIMITATIONS" .SS "Portability" .IX Subsection "Portability" Portability is a goal, not a guarantee. \f(CW\*(C`tee\*(C'\fR requires fork, except on Windows where \f(CW\*(C`system(1, @cmd)\*(C'\fR is used instead. Not tested on any particularly esoteric platforms yet. See the \&\s-1CPAN\s0 Testers Matrix <http://matrix.cpantesters.org/?dist=Capture-Tiny> for test result by platform. .SS "PerlIO layers" .IX Subsection "PerlIO layers" Capture::Tiny does its best to preserve PerlIO layers such as ':utf8' or \&':crlf' when capturing (only for Perl 5.8.1+) . Layers should be applied to \&\s-1STDOUT\s0 or \s-1STDERR\s0 \fIbefore\fR the call to \f(CW\*(C`capture\*(C'\fR or \f(CW\*(C`tee\*(C'\fR. This may not work for tied filehandles (see below). .SS "Modifying filehandles before capturing" .IX Subsection "Modifying filehandles before capturing" Generally speaking, you should do little or no manipulation of the standard \s-1IO\s0 filehandles prior to using Capture::Tiny. In particular, closing, reopening, localizing or tying standard filehandles prior to capture may cause a variety of unexpected, undesirable and/or unreliable behaviors, as described below. Capture::Tiny does its best to compensate for these situations, but the results may not be what you desire. .PP \fIClosed filehandles\fR .IX Subsection "Closed filehandles" .PP Capture::Tiny will work even if \s-1STDIN, STDOUT\s0 or \s-1STDERR\s0 have been previously closed. However, since they will be reopened to capture or tee output, any code within the captured block that depends on finding them closed will, of course, not find them to be closed. If they started closed, Capture::Tiny will close them again when the capture block finishes. .PP Note that this reopening will happen even for \s-1STDIN\s0 or a filehandle not being captured to ensure that the filehandle used for capture is not opened to file descriptor 0, as this causes problems on various platforms. .PP Prior to Perl 5.12, closed \s-1STDIN\s0 combined with PERL_UNICODE=D leaks filehandles and also breaks \fBtee()\fR for undiagnosed reasons. So don't do that. .PP \fILocalized filehandles\fR .IX Subsection "Localized filehandles" .PP If code localizes any of Perl's standard filehandles before capturing, the capture will affect the localized filehandles and not the original ones. External system calls are not affected by localizing a filehandle in Perl and will continue to send output to the original filehandles (which will thus not be captured). .PP \fIScalar filehandles\fR .IX Subsection "Scalar filehandles" .PP If \s-1STDOUT\s0 or \s-1STDERR\s0 are reopened to scalar filehandles prior to the call to \&\f(CW\*(C`capture\*(C'\fR or \f(CW\*(C`tee\*(C'\fR, then Capture::Tiny will override the output filehandle for the duration of the \f(CW\*(C`capture\*(C'\fR or \f(CW\*(C`tee\*(C'\fR call and then, for \f(CW\*(C`tee\*(C'\fR, send captured output to the output filehandle after the capture is complete. (Requires Perl 5.8) .PP Capture::Tiny attempts to preserve the semantics of \s-1STDIN\s0 opened to a scalar reference, but note that external processes will not be able to read from such a handle. Capture::Tiny tries to ensure that external processes will read from the null device instead, but this is not guaranteed. .PP \fITied output filehandles\fR .IX Subsection "Tied output filehandles" .PP If \s-1STDOUT\s0 or \s-1STDERR\s0 are tied prior to the call to \f(CW\*(C`capture\*(C'\fR or \f(CW\*(C`tee\*(C'\fR, then Capture::Tiny will attempt to override the tie for the duration of the \&\f(CW\*(C`capture\*(C'\fR or \f(CW\*(C`tee\*(C'\fR call and then send captured output to the tied filehandle after the capture is complete. (Requires Perl 5.8) .PP Capture::Tiny may not succeed resending \s-1UTF\-8\s0 encoded data to a tied \&\s-1STDOUT\s0 or \s-1STDERR\s0 filehandle. Characters may appear as bytes. If the tied filehandle is based on Tie::StdHandle, then Capture::Tiny will attempt to determine appropriate layers like \f(CW\*(C`:utf8\*(C'\fR from the underlying filehandle and do the right thing. .PP \fITied input filehandle\fR .IX Subsection "Tied input filehandle" .PP Capture::Tiny attempts to preserve the semantics of tied \s-1STDIN,\s0 but this requires Perl 5.8 and is not entirely predictable. External processes will not be able to read from such a handle. .PP Unless having \s-1STDIN\s0 tied is crucial, it may be safest to localize \s-1STDIN\s0 when capturing: .PP .Vb 1 \& my ($out, $err) = do { local *STDIN; capture { ... } }; .Ve .SS "Modifying filehandles during a capture" .IX Subsection "Modifying filehandles during a capture" Attempting to modify \s-1STDIN, STDOUT\s0 or \s-1STDERR\s0 \fIduring\fR \f(CW\*(C`capture\*(C'\fR or \f(CW\*(C`tee\*(C'\fR is almost certainly going to cause problems. Don't do that. .PP \fIForking inside a capture\fR .IX Subsection "Forking inside a capture" .PP Forks aren't portable. The behavior of filehandles during a fork is even less so. If Capture::Tiny detects that a fork has occurred within a capture, it will shortcut in the child process and return empty strings for captures. Other problems may occur in the child or parent, as well. Forking in a capture block is not recommended. .PP \fIUsing threads\fR .IX Subsection "Using threads" .PP Filehandles are global. Mixing up I/O and captures in different threads without coordination is going to cause problems. Besides, threads are officially discouraged. .PP \fIDropping privileges during a capture\fR .IX Subsection "Dropping privileges during a capture" .PP If you drop privileges during a capture, temporary files created to facilitate the capture may not be cleaned up afterwards. .SS "No support for Perl 5.8.0" .IX Subsection "No support for Perl 5.8.0" It's just too buggy when it comes to layers and \s-1UTF\-8.\s0 Perl 5.8.1 or later is recommended. .SS "Limited support for Perl 5.6" .IX Subsection "Limited support for Perl 5.6" Perl 5.6 predates PerlIO. \s-1UTF\-8\s0 data may not be captured correctly. .SH "ENVIRONMENT" .IX Header "ENVIRONMENT" .SS "\s-1PERL_CAPTURE_TINY_TIMEOUT\s0" .IX Subsection "PERL_CAPTURE_TINY_TIMEOUT" Capture::Tiny uses subprocesses internally for \f(CW\*(C`tee\*(C'\fR. By default, Capture::Tiny will timeout with an error if such subprocesses are not ready to receive data within 30 seconds (or whatever is the value of \&\f(CW$Capture::Tiny::TIMEOUT\fR). An alternate timeout may be specified by setting the \f(CW\*(C`PERL_CAPTURE_TINY_TIMEOUT\*(C'\fR environment variable. Setting it to zero will disable timeouts. \fB\s-1NOTE\s0\fR, this does not timeout the code reference being captured \*(-- this only prevents Capture::Tiny itself from hanging your process waiting for its child processes to be ready to proceed. .SH "SEE ALSO" .IX Header "SEE ALSO" This module was inspired by IO::CaptureOutput, which provides similar functionality without the ability to tee output and with more complicated code and \s-1API.\s0 IO::CaptureOutput does not handle layers or most of the unusual cases described in the \*(L"Limitations\*(R" section and I no longer recommend it. .PP There are many other \s-1CPAN\s0 modules that provide some sort of output capture, albeit with various limitations that make them appropriate only in particular circumstances. I'm probably missing some. The long list is provided to show why I felt Capture::Tiny was necessary. .IP "\(bu" 4 IO::Capture .IP "\(bu" 4 IO::Capture::Extended .IP "\(bu" 4 IO::CaptureOutput .IP "\(bu" 4 IPC::Capture .IP "\(bu" 4 IPC::Cmd .IP "\(bu" 4 IPC::Open2 .IP "\(bu" 4 IPC::Open3 .IP "\(bu" 4 IPC::Open3::Simple .IP "\(bu" 4 IPC::Open3::Utils .IP "\(bu" 4 IPC::Run .IP "\(bu" 4 IPC::Run::SafeHandles .IP "\(bu" 4 IPC::Run::Simple .IP "\(bu" 4 IPC::Run3 .IP "\(bu" 4 IPC::System::Simple .IP "\(bu" 4 Tee .IP "\(bu" 4 IO::Tee .IP "\(bu" 4 File::Tee .IP "\(bu" 4 Filter::Handle .IP "\(bu" 4 Tie::STDERR .IP "\(bu" 4 Tie::STDOUT .IP "\(bu" 4 Test::Output .SH "SUPPORT" .IX Header "SUPPORT" .SS "Bugs / Feature Requests" .IX Subsection "Bugs / Feature Requests" Please report any bugs or feature requests through the issue tracker at <https://github.com/dagolden/Capture\-Tiny/issues>. You will be notified automatically of any progress on your issue. .SS "Source Code" .IX Subsection "Source Code" This is open source software. The code repository is available for public review and contribution under the terms of the license. .PP <https://github.com/dagolden/Capture\-Tiny> .PP .Vb 1 \& git clone https://github.com/dagolden/Capture\-Tiny.git .Ve .SH "AUTHOR" .IX Header "AUTHOR" David Golden <dagolden@cpan.org> .SH "CONTRIBUTORS" .IX Header "CONTRIBUTORS" .IP "\(bu" 4 Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> .IP "\(bu" 4 David E. Wheeler <david@justatheory.com> .IP "\(bu" 4 fecundf <not.com+github@gmail.com> .IP "\(bu" 4 Graham Knop <haarg@haarg.org> .IP "\(bu" 4 Peter Rabbitson <ribasushi@cpan.org> .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is Copyright (c) 2009 by David Golden. .PP This is free software, licensed under: .PP .Vb 1 \& The Apache License, Version 2.0, January 2004 .Ve blib/man3/.exists 0000644 00000000000 15043642076 0007627 0 ustar 00 blib/man1/.exists 0000644 00000000000 15043642076 0007625 0 ustar 00 blib/arch/.exists 0000644 00000000000 15043642076 0007706 0 ustar 00 blib/arch/auto/Capture/Tiny/.exists 0000644 00000000000 15043642076 0013204 0 ustar 00 blib/bin/.exists 0000644 00000000000 15043642076 0007541 0 ustar 00 blib/script/.exists 0000644 00000000000 15043642076 0010275 0 ustar 00 blib/lib/auto/Capture/Tiny/.exists 0000644 00000000000 15043642076 0013035 0 ustar 00 blib/lib/Capture/.exists 0000644 00000000000 15043642076 0011142 0 ustar 00 blib/lib/Capture/Tiny.pm 0000444 00000071730 15043642076 0011123 0 ustar 00 use 5.006; use strict; use warnings; package Capture::Tiny; # ABSTRACT: Capture STDOUT and STDERR from Perl, XS or external programs our $VERSION = '0.48'; use Carp (); use Exporter (); use IO::Handle (); use File::Spec (); use File::Temp qw/tempfile tmpnam/; use Scalar::Util qw/reftype blessed/; # Get PerlIO or fake it BEGIN { local $@; eval { require PerlIO; PerlIO->can('get_layers') } or *PerlIO::get_layers = sub { return () }; } #--------------------------------------------------------------------------# # create API subroutines and export them # [do STDOUT flag, do STDERR flag, do merge flag, do tee flag] #--------------------------------------------------------------------------# my %api = ( capture => [1,1,0,0], capture_stdout => [1,0,0,0], capture_stderr => [0,1,0,0], capture_merged => [1,1,1,0], tee => [1,1,0,1], tee_stdout => [1,0,0,1], tee_stderr => [0,1,0,1], tee_merged => [1,1,1,1], ); for my $sub ( keys %api ) { my $args = join q{, }, @{$api{$sub}}; eval "sub $sub(&;@) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic } our @ISA = qw/Exporter/; our @EXPORT_OK = keys %api; our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); #--------------------------------------------------------------------------# # constants and fixtures #--------------------------------------------------------------------------# my $IS_WIN32 = $^O eq 'MSWin32'; ##our $DEBUG = $ENV{PERL_CAPTURE_TINY_DEBUG}; ## ##my $DEBUGFH; ##open $DEBUGFH, "> DEBUG" if $DEBUG; ## ##*_debug = $DEBUG ? sub(@) { print {$DEBUGFH} @_ } : sub(){0}; our $TIMEOUT = 30; #--------------------------------------------------------------------------# # command to tee output -- the argument is a filename that must # be opened to signal that the process is ready to receive input. # This is annoying, but seems to be the best that can be done # as a simple, portable IPC technique #--------------------------------------------------------------------------# my @cmd = ($^X, '-C0', '-e', <<'HERE'); use Fcntl; $SIG{HUP}=sub{exit}; if ( my $fn=shift ) { sysopen(my $fh, qq{$fn}, O_WRONLY|O_CREAT|O_EXCL) or die $!; print {$fh} $$; close $fh; } my $buf; while (sysread(STDIN, $buf, 2048)) { syswrite(STDOUT, $buf); syswrite(STDERR, $buf); } HERE #--------------------------------------------------------------------------# # filehandle manipulation #--------------------------------------------------------------------------# sub _relayer { my ($fh, $apply_layers) = @_; # _debug("# requested layers (@{$layers}) for @{[fileno $fh]}\n"); # eliminate pseudo-layers binmode( $fh, ":raw" ); # strip off real layers until only :unix is left while ( 1 < ( my $layers =()= PerlIO::get_layers( $fh, output => 1 ) ) ) { binmode( $fh, ":pop" ); } # apply other layers my @to_apply = @$apply_layers; shift @to_apply; # eliminate initial :unix # _debug("# applying layers (unix @to_apply) to @{[fileno $fh]}\n"); binmode($fh, ":" . join(":",@to_apply)); } sub _name { my $glob = shift; no strict 'refs'; ## no critic return *{$glob}{NAME}; } sub _open { open $_[0], $_[1] or Carp::confess "Error from open(" . join(q{, }, @_) . "): $!"; # _debug( "# open " . join( ", " , map { defined $_ ? _name($_) : 'undef' } @_ ) . " as " . fileno( $_[0] ) . "\n" ); } sub _close { # _debug( "# closing " . ( defined $_[0] ? _name($_[0]) : 'undef' ) . " on " . fileno( $_[0] ) . "\n" ); close $_[0] or Carp::confess "Error from close(" . join(q{, }, @_) . "): $!"; } my %dup; # cache this so STDIN stays fd0 my %proxy_count; sub _proxy_std { my %proxies; if ( ! defined fileno STDIN ) { $proxy_count{stdin}++; if (defined $dup{stdin}) { _open \*STDIN, "<&=" . fileno($dup{stdin}); # _debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); } else { _open \*STDIN, "<" . File::Spec->devnull; # _debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); _open $dup{stdin} = IO::Handle->new, "<&=STDIN"; } $proxies{stdin} = \*STDIN; binmode(STDIN, ':utf8') if $] >= 5.008; ## no critic } if ( ! defined fileno STDOUT ) { $proxy_count{stdout}++; if (defined $dup{stdout}) { _open \*STDOUT, ">&=" . fileno($dup{stdout}); # _debug( "# restored proxy STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); } else { _open \*STDOUT, ">" . File::Spec->devnull; # _debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); _open $dup{stdout} = IO::Handle->new, ">&=STDOUT"; } $proxies{stdout} = \*STDOUT; binmode(STDOUT, ':utf8') if $] >= 5.008; ## no critic } if ( ! defined fileno STDERR ) { $proxy_count{stderr}++; if (defined $dup{stderr}) { _open \*STDERR, ">&=" . fileno($dup{stderr}); # _debug( "# restored proxy STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); } else { _open \*STDERR, ">" . File::Spec->devnull; # _debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); _open $dup{stderr} = IO::Handle->new, ">&=STDERR"; } $proxies{stderr} = \*STDERR; binmode(STDERR, ':utf8') if $] >= 5.008; ## no critic } return %proxies; } sub _unproxy { my (%proxies) = @_; # _debug( "# unproxying: " . join(" ", keys %proxies) . "\n" ); for my $p ( keys %proxies ) { $proxy_count{$p}--; # _debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" ); if ( ! $proxy_count{$p} ) { _close $proxies{$p}; _close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup delete $dup{$p}; } } } sub _copy_std { my %handles; for my $h ( qw/stdout stderr stdin/ ) { next if $h eq 'stdin' && ! $IS_WIN32; # WIN32 hangs on tee without STDIN copied my $redir = $h eq 'stdin' ? "<&" : ">&"; _open $handles{$h} = IO::Handle->new(), $redir . uc($h); # ">&STDOUT" or "<&STDIN" } return \%handles; } # In some cases we open all (prior to forking) and in others we only open # the output handles (setting up redirection) sub _open_std { my ($handles) = @_; _open \*STDIN, "<&" . fileno $handles->{stdin} if defined $handles->{stdin}; _open \*STDOUT, ">&" . fileno $handles->{stdout} if defined $handles->{stdout}; _open \*STDERR, ">&" . fileno $handles->{stderr} if defined $handles->{stderr}; } #--------------------------------------------------------------------------# # private subs #--------------------------------------------------------------------------# sub _start_tee { my ($which, $stash) = @_; # $which is "stdout" or "stderr" # setup pipes $stash->{$_}{$which} = IO::Handle->new for qw/tee reader/; pipe $stash->{reader}{$which}, $stash->{tee}{$which}; # _debug( "# pipe for $which\: " . _name($stash->{tee}{$which}) . " " . fileno( $stash->{tee}{$which} ) . " => " . _name($stash->{reader}{$which}) . " " . fileno( $stash->{reader}{$which}) . "\n" ); select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush # setup desired redirection for parent and child $stash->{new}{$which} = $stash->{tee}{$which}; $stash->{child}{$which} = { stdin => $stash->{reader}{$which}, stdout => $stash->{old}{$which}, stderr => $stash->{capture}{$which}, }; # flag file is used to signal the child is ready $stash->{flag_files}{$which} = scalar( tmpnam() ) . $$; # execute @cmd as a separate process if ( $IS_WIN32 ) { my $old_eval_err=$@; undef $@; eval "use Win32API::File qw/GetOsFHandle SetHandleInformation fileLastError HANDLE_FLAG_INHERIT INVALID_HANDLE_VALUE/ "; # _debug( "# Win32API::File loaded\n") unless $@; my $os_fhandle = GetOsFHandle( $stash->{tee}{$which} ); # _debug( "# Couldn't get OS handle: " . fileLastError() . "\n") if ! defined $os_fhandle || $os_fhandle == INVALID_HANDLE_VALUE(); my $result = SetHandleInformation( $os_fhandle, HANDLE_FLAG_INHERIT(), 0); # _debug( $result ? "# set no-inherit flag on $which tee\n" : ("# can't disable tee handle flag inherit: " . fileLastError() . "\n")); _open_std( $stash->{child}{$which} ); $stash->{pid}{$which} = system(1, @cmd, $stash->{flag_files}{$which}); # not restoring std here as it all gets redirected again shortly anyway $@=$old_eval_err; } else { # use fork _fork_exec( $which, $stash ); } } sub _fork_exec { my ($which, $stash) = @_; # $which is "stdout" or "stderr" my $pid = fork; if ( not defined $pid ) { Carp::confess "Couldn't fork(): $!"; } elsif ($pid == 0) { # child # _debug( "# in child process ...\n" ); untie *STDIN; untie *STDOUT; untie *STDERR; _close $stash->{tee}{$which}; # _debug( "# redirecting handles in child ...\n" ); _open_std( $stash->{child}{$which} ); # _debug( "# calling exec on command ...\n" ); exec @cmd, $stash->{flag_files}{$which}; } $stash->{pid}{$which} = $pid } my $have_usleep = eval "use Time::HiRes 'usleep'; 1"; sub _files_exist { return 1 if @_ == grep { -f } @_; Time::HiRes::usleep(1000) if $have_usleep; return 0; } sub _wait_for_tees { my ($stash) = @_; my $start = time; my @files = values %{$stash->{flag_files}}; my $timeout = defined $ENV{PERL_CAPTURE_TINY_TIMEOUT} ? $ENV{PERL_CAPTURE_TINY_TIMEOUT} : $TIMEOUT; 1 until _files_exist(@files) || ($timeout && (time - $start > $timeout)); Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files); unlink $_ for @files; } sub _kill_tees { my ($stash) = @_; if ( $IS_WIN32 ) { # _debug( "# closing handles\n"); close($_) for values %{ $stash->{tee} }; # _debug( "# waiting for subprocesses to finish\n"); my $start = time; 1 until wait == -1 || (time - $start > 30); } else { _close $_ for values %{ $stash->{tee} }; waitpid $_, 0 for values %{ $stash->{pid} }; } } sub _slurp { my ($name, $stash) = @_; my ($fh, $pos) = map { $stash->{$_}{$name} } qw/capture pos/; # _debug( "# slurping captured $name from " . fileno($fh) . " at pos $pos with layers: @{[PerlIO::get_layers($fh)]}\n"); seek( $fh, $pos, 0 ) or die "Couldn't seek on capture handle for $name\n"; my $text = do { local $/; scalar readline $fh }; return defined($text) ? $text : ""; } #--------------------------------------------------------------------------# # _capture_tee() -- generic main sub for capturing or teeing #--------------------------------------------------------------------------# sub _capture_tee { # _debug( "# starting _capture_tee with (@_)...\n" ); my ($do_stdout, $do_stderr, $do_merge, $do_tee, $code, @opts) = @_; my %do = ($do_stdout ? (stdout => 1) : (), $do_stderr ? (stderr => 1) : ()); Carp::confess("Custom capture options must be given as key/value pairs\n") unless @opts % 2 == 0; my $stash = { capture => { @opts } }; for ( keys %{$stash->{capture}} ) { my $fh = $stash->{capture}{$_}; Carp::confess "Custom handle for $_ must be seekable\n" unless ref($fh) eq 'GLOB' || (blessed($fh) && $fh->isa("IO::Seekable")); } # save existing filehandles and setup captures local *CT_ORIG_STDIN = *STDIN ; local *CT_ORIG_STDOUT = *STDOUT; local *CT_ORIG_STDERR = *STDERR; # find initial layers my %layers = ( stdin => [PerlIO::get_layers(\*STDIN) ], stdout => [PerlIO::get_layers(\*STDOUT, output => 1)], stderr => [PerlIO::get_layers(\*STDERR, output => 1)], ); # _debug( "# existing layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; # get layers from underlying glob of tied filehandles if we can # (this only works for things that work like Tie::StdHandle) $layers{stdout} = [PerlIO::get_layers(tied *STDOUT)] if tied(*STDOUT) && (reftype tied *STDOUT eq 'GLOB'); $layers{stderr} = [PerlIO::get_layers(tied *STDERR)] if tied(*STDERR) && (reftype tied *STDERR eq 'GLOB'); # _debug( "# tied object corrected layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; # bypass scalar filehandles and tied handles # localize scalar STDIN to get a proxy to pick up FD0, then restore later to CT_ORIG_STDIN my %localize; $localize{stdin}++, local(*STDIN) if grep { $_ eq 'scalar' } @{$layers{stdin}}; $localize{stdout}++, local(*STDOUT) if $do_stdout && grep { $_ eq 'scalar' } @{$layers{stdout}}; $localize{stderr}++, local(*STDERR) if ($do_stderr || $do_merge) && grep { $_ eq 'scalar' } @{$layers{stderr}}; $localize{stdin}++, local(*STDIN), _open( \*STDIN, "<&=0") if tied *STDIN && $] >= 5.008; $localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") if $do_stdout && tied *STDOUT && $] >= 5.008; $localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2") if ($do_stderr || $do_merge) && tied *STDERR && $] >= 5.008; # _debug( "# localized $_\n" ) for keys %localize; # proxy any closed/localized handles so we don't use fds 0, 1 or 2 my %proxy_std = _proxy_std(); # _debug( "# proxy std: @{ [%proxy_std] }\n" ); # update layers after any proxying $layers{stdout} = [PerlIO::get_layers(\*STDOUT, output => 1)] if $proxy_std{stdout}; $layers{stderr} = [PerlIO::get_layers(\*STDERR, output => 1)] if $proxy_std{stderr}; # _debug( "# post-proxy layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; # store old handles and setup handles for capture $stash->{old} = _copy_std(); $stash->{new} = { %{$stash->{old}} }; # default to originals for ( keys %do ) { $stash->{new}{$_} = ($stash->{capture}{$_} ||= File::Temp->new); seek( $stash->{capture}{$_}, 0, 2 ) or die "Could not seek on capture handle for $_\n"; $stash->{pos}{$_} = tell $stash->{capture}{$_}; # _debug("# will capture $_ on " . fileno($stash->{capture}{$_})."\n" ); _start_tee( $_ => $stash ) if $do_tee; # tees may change $stash->{new} } _wait_for_tees( $stash ) if $do_tee; # finalize redirection $stash->{new}{stderr} = $stash->{new}{stdout} if $do_merge; # _debug( "# redirecting in parent ...\n" ); _open_std( $stash->{new} ); # execute user provided code my ($exit_code, $inner_error, $outer_error, $orig_pid, @result); { $orig_pid = $$; local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN # _debug( "# finalizing layers ...\n" ); _relayer(\*STDOUT, $layers{stdout}) if $do_stdout; _relayer(\*STDERR, $layers{stderr}) if $do_stderr; # _debug( "# running code $code ...\n" ); my $old_eval_err=$@; undef $@; eval { @result = $code->(); $inner_error = $@ }; $exit_code = $?; # save this for later $outer_error = $@; # save this for later STDOUT->flush if $do_stdout; STDERR->flush if $do_stderr; $@ = $old_eval_err; } # restore prior filehandles and shut down tees # _debug( "# restoring filehandles ...\n" ); _open_std( $stash->{old} ); _close( $_ ) for values %{$stash->{old}}; # don't leak fds # shouldn't need relayering originals, but see rt.perl.org #114404 _relayer(\*STDOUT, $layers{stdout}) if $do_stdout; _relayer(\*STDERR, $layers{stderr}) if $do_stderr; _unproxy( %proxy_std ); # _debug( "# killing tee subprocesses ...\n" ) if $do_tee; _kill_tees( $stash ) if $do_tee; # return captured output, but shortcut in void context # unless we have to echo output to tied/scalar handles; my %got; if ( $orig_pid == $$ and ( defined wantarray or ($do_tee && keys %localize) ) ) { for ( keys %do ) { _relayer($stash->{capture}{$_}, $layers{$_}); $got{$_} = _slurp($_, $stash); # _debug("# slurped " . length($got{$_}) . " bytes from $_\n"); } print CT_ORIG_STDOUT $got{stdout} if $do_stdout && $do_tee && $localize{stdout}; print CT_ORIG_STDERR $got{stderr} if $do_stderr && $do_tee && $localize{stderr}; } $? = $exit_code; $@ = $inner_error if $inner_error; die $outer_error if $outer_error; # _debug( "# ending _capture_tee with (@_)...\n" ); return unless defined wantarray; my @return; push @return, $got{stdout} if $do_stdout; push @return, $got{stderr} if $do_stderr && ! $do_merge; push @return, @result; return wantarray ? @return : $return[0]; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Capture::Tiny - Capture STDOUT and STDERR from Perl, XS or external programs =head1 VERSION version 0.48 =head1 SYNOPSIS use Capture::Tiny ':all'; # capture from external command ($stdout, $stderr, $exit) = capture { system( $cmd, @args ); }; # capture from arbitrary code (Perl or external) ($stdout, $stderr, @result) = capture { # your code here }; # capture partial or merged output $stdout = capture_stdout { ... }; $stderr = capture_stderr { ... }; $merged = capture_merged { ... }; # tee output ($stdout, $stderr) = tee { # your code here }; $stdout = tee_stdout { ... }; $stderr = tee_stderr { ... }; $merged = tee_merged { ... }; =head1 DESCRIPTION Capture::Tiny provides a simple, portable way to capture almost anything sent to STDOUT or STDERR, regardless of whether it comes from Perl, from XS code or from an external program. Optionally, output can be teed so that it is captured while being passed through to the original filehandles. Yes, it even works on Windows (usually). Stop guessing which of a dozen capturing modules to use in any particular situation and just use this one. =head1 USAGE The following functions are available. None are exported by default. =head2 capture ($stdout, $stderr, @result) = capture \&code; $stdout = capture \&code; The C<capture> function takes a code reference and returns what is sent to STDOUT and STDERR as well as any return values from the code reference. In scalar context, it returns only STDOUT. If no output was received for a filehandle, it returns an empty string for that filehandle. Regardless of calling context, all output is captured -- nothing is passed to the existing filehandles. It is prototyped to take a subroutine reference as an argument. Thus, it can be called in block form: ($stdout, $stderr) = capture { # your code here ... }; Note that the coderef is evaluated in list context. If you wish to force scalar context on the return value, you must use the C<scalar> keyword. ($stdout, $stderr, $count) = capture { my @list = qw/one two three/; return scalar @list; # $count will be 3 }; Also note that within the coderef, the C<@_> variable will be empty. So don't use arguments from a surrounding subroutine without copying them to an array first: sub wont_work { my ($stdout, $stderr) = capture { do_stuff( @_ ) }; # WRONG ... } sub will_work { my @args = @_; my ($stdout, $stderr) = capture { do_stuff( @args ) }; # RIGHT ... } Captures are normally done to an anonymous temporary filehandle. To capture via a named file (e.g. to externally monitor a long-running capture), provide custom filehandles as a trailing list of option pairs: my $out_fh = IO::File->new("out.txt", "w+"); my $err_fh = IO::File->new("out.txt", "w+"); capture { ... } stdout => $out_fh, stderr => $err_fh; The filehandles must be read/write and seekable. Modifying the files or filehandles during a capture operation will give unpredictable results. Existing IO layers on them may be changed by the capture. When called in void context, C<capture> saves memory and time by not reading back from the capture handles. =head2 capture_stdout ($stdout, @result) = capture_stdout \&code; $stdout = capture_stdout \&code; The C<capture_stdout> function works just like C<capture> except only STDOUT is captured. STDERR is not captured. =head2 capture_stderr ($stderr, @result) = capture_stderr \&code; $stderr = capture_stderr \&code; The C<capture_stderr> function works just like C<capture> except only STDERR is captured. STDOUT is not captured. =head2 capture_merged ($merged, @result) = capture_merged \&code; $merged = capture_merged \&code; The C<capture_merged> function works just like C<capture> except STDOUT and STDERR are merged. (Technically, STDERR is redirected to the same capturing handle as STDOUT before executing the function.) Caution: STDOUT and STDERR output in the merged result are not guaranteed to be properly ordered due to buffering. =head2 tee ($stdout, $stderr, @result) = tee \&code; $stdout = tee \&code; The C<tee> function works just like C<capture>, except that output is captured as well as passed on to the original STDOUT and STDERR. When called in void context, C<tee> saves memory and time by not reading back from the capture handles, except when the original STDOUT OR STDERR were tied or opened to a scalar handle. =head2 tee_stdout ($stdout, @result) = tee_stdout \&code; $stdout = tee_stdout \&code; The C<tee_stdout> function works just like C<tee> except only STDOUT is teed. STDERR is not teed (output goes to STDERR as usual). =head2 tee_stderr ($stderr, @result) = tee_stderr \&code; $stderr = tee_stderr \&code; The C<tee_stderr> function works just like C<tee> except only STDERR is teed. STDOUT is not teed (output goes to STDOUT as usual). =head2 tee_merged ($merged, @result) = tee_merged \&code; $merged = tee_merged \&code; The C<tee_merged> function works just like C<capture_merged> except that output is captured as well as passed on to STDOUT. Caution: STDOUT and STDERR output in the merged result are not guaranteed to be properly ordered due to buffering. =head1 LIMITATIONS =head2 Portability Portability is a goal, not a guarantee. C<tee> requires fork, except on Windows where C<system(1, @cmd)> is used instead. Not tested on any particularly esoteric platforms yet. See the L<CPAN Testers Matrix|http://matrix.cpantesters.org/?dist=Capture-Tiny> for test result by platform. =head2 PerlIO layers Capture::Tiny does its best to preserve PerlIO layers such as ':utf8' or ':crlf' when capturing (only for Perl 5.8.1+) . Layers should be applied to STDOUT or STDERR I<before> the call to C<capture> or C<tee>. This may not work for tied filehandles (see below). =head2 Modifying filehandles before capturing Generally speaking, you should do little or no manipulation of the standard IO filehandles prior to using Capture::Tiny. In particular, closing, reopening, localizing or tying standard filehandles prior to capture may cause a variety of unexpected, undesirable and/or unreliable behaviors, as described below. Capture::Tiny does its best to compensate for these situations, but the results may not be what you desire. =head3 Closed filehandles Capture::Tiny will work even if STDIN, STDOUT or STDERR have been previously closed. However, since they will be reopened to capture or tee output, any code within the captured block that depends on finding them closed will, of course, not find them to be closed. If they started closed, Capture::Tiny will close them again when the capture block finishes. Note that this reopening will happen even for STDIN or a filehandle not being captured to ensure that the filehandle used for capture is not opened to file descriptor 0, as this causes problems on various platforms. Prior to Perl 5.12, closed STDIN combined with PERL_UNICODE=D leaks filehandles and also breaks tee() for undiagnosed reasons. So don't do that. =head3 Localized filehandles If code localizes any of Perl's standard filehandles before capturing, the capture will affect the localized filehandles and not the original ones. External system calls are not affected by localizing a filehandle in Perl and will continue to send output to the original filehandles (which will thus not be captured). =head3 Scalar filehandles If STDOUT or STDERR are reopened to scalar filehandles prior to the call to C<capture> or C<tee>, then Capture::Tiny will override the output filehandle for the duration of the C<capture> or C<tee> call and then, for C<tee>, send captured output to the output filehandle after the capture is complete. (Requires Perl 5.8) Capture::Tiny attempts to preserve the semantics of STDIN opened to a scalar reference, but note that external processes will not be able to read from such a handle. Capture::Tiny tries to ensure that external processes will read from the null device instead, but this is not guaranteed. =head3 Tied output filehandles If STDOUT or STDERR are tied prior to the call to C<capture> or C<tee>, then Capture::Tiny will attempt to override the tie for the duration of the C<capture> or C<tee> call and then send captured output to the tied filehandle after the capture is complete. (Requires Perl 5.8) Capture::Tiny may not succeed resending UTF-8 encoded data to a tied STDOUT or STDERR filehandle. Characters may appear as bytes. If the tied filehandle is based on L<Tie::StdHandle>, then Capture::Tiny will attempt to determine appropriate layers like C<:utf8> from the underlying filehandle and do the right thing. =head3 Tied input filehandle Capture::Tiny attempts to preserve the semantics of tied STDIN, but this requires Perl 5.8 and is not entirely predictable. External processes will not be able to read from such a handle. Unless having STDIN tied is crucial, it may be safest to localize STDIN when capturing: my ($out, $err) = do { local *STDIN; capture { ... } }; =head2 Modifying filehandles during a capture Attempting to modify STDIN, STDOUT or STDERR I<during> C<capture> or C<tee> is almost certainly going to cause problems. Don't do that. =head3 Forking inside a capture Forks aren't portable. The behavior of filehandles during a fork is even less so. If Capture::Tiny detects that a fork has occurred within a capture, it will shortcut in the child process and return empty strings for captures. Other problems may occur in the child or parent, as well. Forking in a capture block is not recommended. =head3 Using threads Filehandles are global. Mixing up I/O and captures in different threads without coordination is going to cause problems. Besides, threads are officially discouraged. =head3 Dropping privileges during a capture If you drop privileges during a capture, temporary files created to facilitate the capture may not be cleaned up afterwards. =head2 No support for Perl 5.8.0 It's just too buggy when it comes to layers and UTF-8. Perl 5.8.1 or later is recommended. =head2 Limited support for Perl 5.6 Perl 5.6 predates PerlIO. UTF-8 data may not be captured correctly. =head1 ENVIRONMENT =head2 PERL_CAPTURE_TINY_TIMEOUT Capture::Tiny uses subprocesses internally for C<tee>. By default, Capture::Tiny will timeout with an error if such subprocesses are not ready to receive data within 30 seconds (or whatever is the value of C<$Capture::Tiny::TIMEOUT>). An alternate timeout may be specified by setting the C<PERL_CAPTURE_TINY_TIMEOUT> environment variable. Setting it to zero will disable timeouts. B<NOTE>, this does not timeout the code reference being captured -- this only prevents Capture::Tiny itself from hanging your process waiting for its child processes to be ready to proceed. =head1 SEE ALSO This module was inspired by L<IO::CaptureOutput>, which provides similar functionality without the ability to tee output and with more complicated code and API. L<IO::CaptureOutput> does not handle layers or most of the unusual cases described in the L</Limitations> section and I no longer recommend it. There are many other CPAN modules that provide some sort of output capture, albeit with various limitations that make them appropriate only in particular circumstances. I'm probably missing some. The long list is provided to show why I felt Capture::Tiny was necessary. =over 4 =item * L<IO::Capture> =item * L<IO::Capture::Extended> =item * L<IO::CaptureOutput> =item * L<IPC::Capture> =item * L<IPC::Cmd> =item * L<IPC::Open2> =item * L<IPC::Open3> =item * L<IPC::Open3::Simple> =item * L<IPC::Open3::Utils> =item * L<IPC::Run> =item * L<IPC::Run::SafeHandles> =item * L<IPC::Run::Simple> =item * L<IPC::Run3> =item * L<IPC::System::Simple> =item * L<Tee> =item * L<IO::Tee> =item * L<File::Tee> =item * L<Filter::Handle> =item * L<Tie::STDERR> =item * L<Tie::STDOUT> =item * L<Test::Output> =back =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan =head1 SUPPORT =head2 Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at L<https://github.com/dagolden/Capture-Tiny/issues>. You will be notified automatically of any progress on your issue. =head2 Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. L<https://github.com/dagolden/Capture-Tiny> git clone https://github.com/dagolden/Capture-Tiny.git =head1 AUTHOR David Golden <dagolden@cpan.org> =head1 CONTRIBUTORS =for stopwords Dagfinn Ilmari Mannsåker David E. Wheeler fecundf Graham Knop Peter Rabbitson =over 4 =item * Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> =item * David E. Wheeler <david@justatheory.com> =item * fecundf <not.com+github@gmail.com> =item * Graham Knop <haarg@haarg.org> =item * Peter Rabbitson <ribasushi@cpan.org> =back =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2009 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 =cut MYMETA.yml 0000644 00000002546 15043642076 0006301 0 ustar 00 --- abstract: 'Capture STDOUT and STDERR from Perl, XS or external programs' author: - 'David Golden <dagolden@cpan.org>' build_requires: ExtUtils::MakeMaker: '0' File::Spec: '0' IO::File: '0' Test::More: '0.62' lib: '0' configure_requires: ExtUtils::MakeMaker: '6.17' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010' license: apache meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Capture-Tiny no_index: directory: - corpus - examples - t - xt package: - DB provides: Capture::Tiny: file: lib/Capture/Tiny.pm version: '0.48' requires: Carp: '0' Exporter: '0' File::Spec: '0' File::Temp: '0' IO::Handle: '0' Scalar::Util: '0' perl: '5.006' strict: '0' warnings: '0' resources: bugtracker: https://github.com/dagolden/Capture-Tiny/issues homepage: https://github.com/dagolden/Capture-Tiny repository: https://github.com/dagolden/Capture-Tiny.git version: '0.48' x_authority: cpan:DAGOLDEN x_contributors: - 'Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>' - 'David E. Wheeler <david@justatheory.com>' - 'fecundf <not.com+github@gmail.com>' - 'Graham Knop <haarg@haarg.org>' - 'Peter Rabbitson <ribasushi@cpan.org>' x_generated_by_perl: v5.26.1 x_serialization_backend: 'CPAN::Meta::YAML version 0.018' MANIFEST 0000644 00000001773 15043642076 0005714 0 ustar 00 # This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.012. CONTRIBUTING.mkdn Changes LICENSE MANIFEST META.json META.yml Makefile.PL README Todo cpanfile dist.ini examples/rt-58208.pl examples/tee.pl lib/Capture/Tiny.pm perlcritic.rc t/00-report-prereqs.dd t/00-report-prereqs.t t/01-Capture-Tiny.t t/02-capture.t t/03-tee.t t/06-stdout-closed.t t/07-stderr-closed.t t/08-stdin-closed.t t/09-preserve-exit-code.t t/10-stdout-string.t t/11-stderr-string.t t/12-stdin-string.t t/13-stdout-tied.t t/14-stderr-tied.t t/15-stdin-tied.t t/16-catch-errors.t t/17-pass-results.t t/18-custom-capture.t t/19-relayering.t t/20-stdout-badtie.t t/21-stderr-badtie.t t/22-stdin-badtie.t t/23-all-tied.t t/24-all-badtied.t t/25-cap-fork.t t/lib/Cases.pm t/lib/TieEvil.pm t/lib/TieLC.pm t/lib/Utils.pm xt/author/00-compile.t xt/author/critic.t xt/author/minimum-version.t xt/author/pod-coverage.t xt/author/pod-spell.t xt/author/pod-syntax.t xt/author/portability.t xt/author/test-version.t xt/release/distmeta.t lib/Capture/Tiny.pm 0000644 00000071730 15043642076 0010215 0 ustar 00 use 5.006; use strict; use warnings; package Capture::Tiny; # ABSTRACT: Capture STDOUT and STDERR from Perl, XS or external programs our $VERSION = '0.48'; use Carp (); use Exporter (); use IO::Handle (); use File::Spec (); use File::Temp qw/tempfile tmpnam/; use Scalar::Util qw/reftype blessed/; # Get PerlIO or fake it BEGIN { local $@; eval { require PerlIO; PerlIO->can('get_layers') } or *PerlIO::get_layers = sub { return () }; } #--------------------------------------------------------------------------# # create API subroutines and export them # [do STDOUT flag, do STDERR flag, do merge flag, do tee flag] #--------------------------------------------------------------------------# my %api = ( capture => [1,1,0,0], capture_stdout => [1,0,0,0], capture_stderr => [0,1,0,0], capture_merged => [1,1,1,0], tee => [1,1,0,1], tee_stdout => [1,0,0,1], tee_stderr => [0,1,0,1], tee_merged => [1,1,1,1], ); for my $sub ( keys %api ) { my $args = join q{, }, @{$api{$sub}}; eval "sub $sub(&;@) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic } our @ISA = qw/Exporter/; our @EXPORT_OK = keys %api; our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); #--------------------------------------------------------------------------# # constants and fixtures #--------------------------------------------------------------------------# my $IS_WIN32 = $^O eq 'MSWin32'; ##our $DEBUG = $ENV{PERL_CAPTURE_TINY_DEBUG}; ## ##my $DEBUGFH; ##open $DEBUGFH, "> DEBUG" if $DEBUG; ## ##*_debug = $DEBUG ? sub(@) { print {$DEBUGFH} @_ } : sub(){0}; our $TIMEOUT = 30; #--------------------------------------------------------------------------# # command to tee output -- the argument is a filename that must # be opened to signal that the process is ready to receive input. # This is annoying, but seems to be the best that can be done # as a simple, portable IPC technique #--------------------------------------------------------------------------# my @cmd = ($^X, '-C0', '-e', <<'HERE'); use Fcntl; $SIG{HUP}=sub{exit}; if ( my $fn=shift ) { sysopen(my $fh, qq{$fn}, O_WRONLY|O_CREAT|O_EXCL) or die $!; print {$fh} $$; close $fh; } my $buf; while (sysread(STDIN, $buf, 2048)) { syswrite(STDOUT, $buf); syswrite(STDERR, $buf); } HERE #--------------------------------------------------------------------------# # filehandle manipulation #--------------------------------------------------------------------------# sub _relayer { my ($fh, $apply_layers) = @_; # _debug("# requested layers (@{$layers}) for @{[fileno $fh]}\n"); # eliminate pseudo-layers binmode( $fh, ":raw" ); # strip off real layers until only :unix is left while ( 1 < ( my $layers =()= PerlIO::get_layers( $fh, output => 1 ) ) ) { binmode( $fh, ":pop" ); } # apply other layers my @to_apply = @$apply_layers; shift @to_apply; # eliminate initial :unix # _debug("# applying layers (unix @to_apply) to @{[fileno $fh]}\n"); binmode($fh, ":" . join(":",@to_apply)); } sub _name { my $glob = shift; no strict 'refs'; ## no critic return *{$glob}{NAME}; } sub _open { open $_[0], $_[1] or Carp::confess "Error from open(" . join(q{, }, @_) . "): $!"; # _debug( "# open " . join( ", " , map { defined $_ ? _name($_) : 'undef' } @_ ) . " as " . fileno( $_[0] ) . "\n" ); } sub _close { # _debug( "# closing " . ( defined $_[0] ? _name($_[0]) : 'undef' ) . " on " . fileno( $_[0] ) . "\n" ); close $_[0] or Carp::confess "Error from close(" . join(q{, }, @_) . "): $!"; } my %dup; # cache this so STDIN stays fd0 my %proxy_count; sub _proxy_std { my %proxies; if ( ! defined fileno STDIN ) { $proxy_count{stdin}++; if (defined $dup{stdin}) { _open \*STDIN, "<&=" . fileno($dup{stdin}); # _debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); } else { _open \*STDIN, "<" . File::Spec->devnull; # _debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); _open $dup{stdin} = IO::Handle->new, "<&=STDIN"; } $proxies{stdin} = \*STDIN; binmode(STDIN, ':utf8') if $] >= 5.008; ## no critic } if ( ! defined fileno STDOUT ) { $proxy_count{stdout}++; if (defined $dup{stdout}) { _open \*STDOUT, ">&=" . fileno($dup{stdout}); # _debug( "# restored proxy STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); } else { _open \*STDOUT, ">" . File::Spec->devnull; # _debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); _open $dup{stdout} = IO::Handle->new, ">&=STDOUT"; } $proxies{stdout} = \*STDOUT; binmode(STDOUT, ':utf8') if $] >= 5.008; ## no critic } if ( ! defined fileno STDERR ) { $proxy_count{stderr}++; if (defined $dup{stderr}) { _open \*STDERR, ">&=" . fileno($dup{stderr}); # _debug( "# restored proxy STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); } else { _open \*STDERR, ">" . File::Spec->devnull; # _debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); _open $dup{stderr} = IO::Handle->new, ">&=STDERR"; } $proxies{stderr} = \*STDERR; binmode(STDERR, ':utf8') if $] >= 5.008; ## no critic } return %proxies; } sub _unproxy { my (%proxies) = @_; # _debug( "# unproxying: " . join(" ", keys %proxies) . "\n" ); for my $p ( keys %proxies ) { $proxy_count{$p}--; # _debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" ); if ( ! $proxy_count{$p} ) { _close $proxies{$p}; _close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup delete $dup{$p}; } } } sub _copy_std { my %handles; for my $h ( qw/stdout stderr stdin/ ) { next if $h eq 'stdin' && ! $IS_WIN32; # WIN32 hangs on tee without STDIN copied my $redir = $h eq 'stdin' ? "<&" : ">&"; _open $handles{$h} = IO::Handle->new(), $redir . uc($h); # ">&STDOUT" or "<&STDIN" } return \%handles; } # In some cases we open all (prior to forking) and in others we only open # the output handles (setting up redirection) sub _open_std { my ($handles) = @_; _open \*STDIN, "<&" . fileno $handles->{stdin} if defined $handles->{stdin}; _open \*STDOUT, ">&" . fileno $handles->{stdout} if defined $handles->{stdout}; _open \*STDERR, ">&" . fileno $handles->{stderr} if defined $handles->{stderr}; } #--------------------------------------------------------------------------# # private subs #--------------------------------------------------------------------------# sub _start_tee { my ($which, $stash) = @_; # $which is "stdout" or "stderr" # setup pipes $stash->{$_}{$which} = IO::Handle->new for qw/tee reader/; pipe $stash->{reader}{$which}, $stash->{tee}{$which}; # _debug( "# pipe for $which\: " . _name($stash->{tee}{$which}) . " " . fileno( $stash->{tee}{$which} ) . " => " . _name($stash->{reader}{$which}) . " " . fileno( $stash->{reader}{$which}) . "\n" ); select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush # setup desired redirection for parent and child $stash->{new}{$which} = $stash->{tee}{$which}; $stash->{child}{$which} = { stdin => $stash->{reader}{$which}, stdout => $stash->{old}{$which}, stderr => $stash->{capture}{$which}, }; # flag file is used to signal the child is ready $stash->{flag_files}{$which} = scalar( tmpnam() ) . $$; # execute @cmd as a separate process if ( $IS_WIN32 ) { my $old_eval_err=$@; undef $@; eval "use Win32API::File qw/GetOsFHandle SetHandleInformation fileLastError HANDLE_FLAG_INHERIT INVALID_HANDLE_VALUE/ "; # _debug( "# Win32API::File loaded\n") unless $@; my $os_fhandle = GetOsFHandle( $stash->{tee}{$which} ); # _debug( "# Couldn't get OS handle: " . fileLastError() . "\n") if ! defined $os_fhandle || $os_fhandle == INVALID_HANDLE_VALUE(); my $result = SetHandleInformation( $os_fhandle, HANDLE_FLAG_INHERIT(), 0); # _debug( $result ? "# set no-inherit flag on $which tee\n" : ("# can't disable tee handle flag inherit: " . fileLastError() . "\n")); _open_std( $stash->{child}{$which} ); $stash->{pid}{$which} = system(1, @cmd, $stash->{flag_files}{$which}); # not restoring std here as it all gets redirected again shortly anyway $@=$old_eval_err; } else { # use fork _fork_exec( $which, $stash ); } } sub _fork_exec { my ($which, $stash) = @_; # $which is "stdout" or "stderr" my $pid = fork; if ( not defined $pid ) { Carp::confess "Couldn't fork(): $!"; } elsif ($pid == 0) { # child # _debug( "# in child process ...\n" ); untie *STDIN; untie *STDOUT; untie *STDERR; _close $stash->{tee}{$which}; # _debug( "# redirecting handles in child ...\n" ); _open_std( $stash->{child}{$which} ); # _debug( "# calling exec on command ...\n" ); exec @cmd, $stash->{flag_files}{$which}; } $stash->{pid}{$which} = $pid } my $have_usleep = eval "use Time::HiRes 'usleep'; 1"; sub _files_exist { return 1 if @_ == grep { -f } @_; Time::HiRes::usleep(1000) if $have_usleep; return 0; } sub _wait_for_tees { my ($stash) = @_; my $start = time; my @files = values %{$stash->{flag_files}}; my $timeout = defined $ENV{PERL_CAPTURE_TINY_TIMEOUT} ? $ENV{PERL_CAPTURE_TINY_TIMEOUT} : $TIMEOUT; 1 until _files_exist(@files) || ($timeout && (time - $start > $timeout)); Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files); unlink $_ for @files; } sub _kill_tees { my ($stash) = @_; if ( $IS_WIN32 ) { # _debug( "# closing handles\n"); close($_) for values %{ $stash->{tee} }; # _debug( "# waiting for subprocesses to finish\n"); my $start = time; 1 until wait == -1 || (time - $start > 30); } else { _close $_ for values %{ $stash->{tee} }; waitpid $_, 0 for values %{ $stash->{pid} }; } } sub _slurp { my ($name, $stash) = @_; my ($fh, $pos) = map { $stash->{$_}{$name} } qw/capture pos/; # _debug( "# slurping captured $name from " . fileno($fh) . " at pos $pos with layers: @{[PerlIO::get_layers($fh)]}\n"); seek( $fh, $pos, 0 ) or die "Couldn't seek on capture handle for $name\n"; my $text = do { local $/; scalar readline $fh }; return defined($text) ? $text : ""; } #--------------------------------------------------------------------------# # _capture_tee() -- generic main sub for capturing or teeing #--------------------------------------------------------------------------# sub _capture_tee { # _debug( "# starting _capture_tee with (@_)...\n" ); my ($do_stdout, $do_stderr, $do_merge, $do_tee, $code, @opts) = @_; my %do = ($do_stdout ? (stdout => 1) : (), $do_stderr ? (stderr => 1) : ()); Carp::confess("Custom capture options must be given as key/value pairs\n") unless @opts % 2 == 0; my $stash = { capture => { @opts } }; for ( keys %{$stash->{capture}} ) { my $fh = $stash->{capture}{$_}; Carp::confess "Custom handle for $_ must be seekable\n" unless ref($fh) eq 'GLOB' || (blessed($fh) && $fh->isa("IO::Seekable")); } # save existing filehandles and setup captures local *CT_ORIG_STDIN = *STDIN ; local *CT_ORIG_STDOUT = *STDOUT; local *CT_ORIG_STDERR = *STDERR; # find initial layers my %layers = ( stdin => [PerlIO::get_layers(\*STDIN) ], stdout => [PerlIO::get_layers(\*STDOUT, output => 1)], stderr => [PerlIO::get_layers(\*STDERR, output => 1)], ); # _debug( "# existing layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; # get layers from underlying glob of tied filehandles if we can # (this only works for things that work like Tie::StdHandle) $layers{stdout} = [PerlIO::get_layers(tied *STDOUT)] if tied(*STDOUT) && (reftype tied *STDOUT eq 'GLOB'); $layers{stderr} = [PerlIO::get_layers(tied *STDERR)] if tied(*STDERR) && (reftype tied *STDERR eq 'GLOB'); # _debug( "# tied object corrected layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; # bypass scalar filehandles and tied handles # localize scalar STDIN to get a proxy to pick up FD0, then restore later to CT_ORIG_STDIN my %localize; $localize{stdin}++, local(*STDIN) if grep { $_ eq 'scalar' } @{$layers{stdin}}; $localize{stdout}++, local(*STDOUT) if $do_stdout && grep { $_ eq 'scalar' } @{$layers{stdout}}; $localize{stderr}++, local(*STDERR) if ($do_stderr || $do_merge) && grep { $_ eq 'scalar' } @{$layers{stderr}}; $localize{stdin}++, local(*STDIN), _open( \*STDIN, "<&=0") if tied *STDIN && $] >= 5.008; $localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") if $do_stdout && tied *STDOUT && $] >= 5.008; $localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2") if ($do_stderr || $do_merge) && tied *STDERR && $] >= 5.008; # _debug( "# localized $_\n" ) for keys %localize; # proxy any closed/localized handles so we don't use fds 0, 1 or 2 my %proxy_std = _proxy_std(); # _debug( "# proxy std: @{ [%proxy_std] }\n" ); # update layers after any proxying $layers{stdout} = [PerlIO::get_layers(\*STDOUT, output => 1)] if $proxy_std{stdout}; $layers{stderr} = [PerlIO::get_layers(\*STDERR, output => 1)] if $proxy_std{stderr}; # _debug( "# post-proxy layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; # store old handles and setup handles for capture $stash->{old} = _copy_std(); $stash->{new} = { %{$stash->{old}} }; # default to originals for ( keys %do ) { $stash->{new}{$_} = ($stash->{capture}{$_} ||= File::Temp->new); seek( $stash->{capture}{$_}, 0, 2 ) or die "Could not seek on capture handle for $_\n"; $stash->{pos}{$_} = tell $stash->{capture}{$_}; # _debug("# will capture $_ on " . fileno($stash->{capture}{$_})."\n" ); _start_tee( $_ => $stash ) if $do_tee; # tees may change $stash->{new} } _wait_for_tees( $stash ) if $do_tee; # finalize redirection $stash->{new}{stderr} = $stash->{new}{stdout} if $do_merge; # _debug( "# redirecting in parent ...\n" ); _open_std( $stash->{new} ); # execute user provided code my ($exit_code, $inner_error, $outer_error, $orig_pid, @result); { $orig_pid = $$; local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN # _debug( "# finalizing layers ...\n" ); _relayer(\*STDOUT, $layers{stdout}) if $do_stdout; _relayer(\*STDERR, $layers{stderr}) if $do_stderr; # _debug( "# running code $code ...\n" ); my $old_eval_err=$@; undef $@; eval { @result = $code->(); $inner_error = $@ }; $exit_code = $?; # save this for later $outer_error = $@; # save this for later STDOUT->flush if $do_stdout; STDERR->flush if $do_stderr; $@ = $old_eval_err; } # restore prior filehandles and shut down tees # _debug( "# restoring filehandles ...\n" ); _open_std( $stash->{old} ); _close( $_ ) for values %{$stash->{old}}; # don't leak fds # shouldn't need relayering originals, but see rt.perl.org #114404 _relayer(\*STDOUT, $layers{stdout}) if $do_stdout; _relayer(\*STDERR, $layers{stderr}) if $do_stderr; _unproxy( %proxy_std ); # _debug( "# killing tee subprocesses ...\n" ) if $do_tee; _kill_tees( $stash ) if $do_tee; # return captured output, but shortcut in void context # unless we have to echo output to tied/scalar handles; my %got; if ( $orig_pid == $$ and ( defined wantarray or ($do_tee && keys %localize) ) ) { for ( keys %do ) { _relayer($stash->{capture}{$_}, $layers{$_}); $got{$_} = _slurp($_, $stash); # _debug("# slurped " . length($got{$_}) . " bytes from $_\n"); } print CT_ORIG_STDOUT $got{stdout} if $do_stdout && $do_tee && $localize{stdout}; print CT_ORIG_STDERR $got{stderr} if $do_stderr && $do_tee && $localize{stderr}; } $? = $exit_code; $@ = $inner_error if $inner_error; die $outer_error if $outer_error; # _debug( "# ending _capture_tee with (@_)...\n" ); return unless defined wantarray; my @return; push @return, $got{stdout} if $do_stdout; push @return, $got{stderr} if $do_stderr && ! $do_merge; push @return, @result; return wantarray ? @return : $return[0]; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Capture::Tiny - Capture STDOUT and STDERR from Perl, XS or external programs =head1 VERSION version 0.48 =head1 SYNOPSIS use Capture::Tiny ':all'; # capture from external command ($stdout, $stderr, $exit) = capture { system( $cmd, @args ); }; # capture from arbitrary code (Perl or external) ($stdout, $stderr, @result) = capture { # your code here }; # capture partial or merged output $stdout = capture_stdout { ... }; $stderr = capture_stderr { ... }; $merged = capture_merged { ... }; # tee output ($stdout, $stderr) = tee { # your code here }; $stdout = tee_stdout { ... }; $stderr = tee_stderr { ... }; $merged = tee_merged { ... }; =head1 DESCRIPTION Capture::Tiny provides a simple, portable way to capture almost anything sent to STDOUT or STDERR, regardless of whether it comes from Perl, from XS code or from an external program. Optionally, output can be teed so that it is captured while being passed through to the original filehandles. Yes, it even works on Windows (usually). Stop guessing which of a dozen capturing modules to use in any particular situation and just use this one. =head1 USAGE The following functions are available. None are exported by default. =head2 capture ($stdout, $stderr, @result) = capture \&code; $stdout = capture \&code; The C<capture> function takes a code reference and returns what is sent to STDOUT and STDERR as well as any return values from the code reference. In scalar context, it returns only STDOUT. If no output was received for a filehandle, it returns an empty string for that filehandle. Regardless of calling context, all output is captured -- nothing is passed to the existing filehandles. It is prototyped to take a subroutine reference as an argument. Thus, it can be called in block form: ($stdout, $stderr) = capture { # your code here ... }; Note that the coderef is evaluated in list context. If you wish to force scalar context on the return value, you must use the C<scalar> keyword. ($stdout, $stderr, $count) = capture { my @list = qw/one two three/; return scalar @list; # $count will be 3 }; Also note that within the coderef, the C<@_> variable will be empty. So don't use arguments from a surrounding subroutine without copying them to an array first: sub wont_work { my ($stdout, $stderr) = capture { do_stuff( @_ ) }; # WRONG ... } sub will_work { my @args = @_; my ($stdout, $stderr) = capture { do_stuff( @args ) }; # RIGHT ... } Captures are normally done to an anonymous temporary filehandle. To capture via a named file (e.g. to externally monitor a long-running capture), provide custom filehandles as a trailing list of option pairs: my $out_fh = IO::File->new("out.txt", "w+"); my $err_fh = IO::File->new("out.txt", "w+"); capture { ... } stdout => $out_fh, stderr => $err_fh; The filehandles must be read/write and seekable. Modifying the files or filehandles during a capture operation will give unpredictable results. Existing IO layers on them may be changed by the capture. When called in void context, C<capture> saves memory and time by not reading back from the capture handles. =head2 capture_stdout ($stdout, @result) = capture_stdout \&code; $stdout = capture_stdout \&code; The C<capture_stdout> function works just like C<capture> except only STDOUT is captured. STDERR is not captured. =head2 capture_stderr ($stderr, @result) = capture_stderr \&code; $stderr = capture_stderr \&code; The C<capture_stderr> function works just like C<capture> except only STDERR is captured. STDOUT is not captured. =head2 capture_merged ($merged, @result) = capture_merged \&code; $merged = capture_merged \&code; The C<capture_merged> function works just like C<capture> except STDOUT and STDERR are merged. (Technically, STDERR is redirected to the same capturing handle as STDOUT before executing the function.) Caution: STDOUT and STDERR output in the merged result are not guaranteed to be properly ordered due to buffering. =head2 tee ($stdout, $stderr, @result) = tee \&code; $stdout = tee \&code; The C<tee> function works just like C<capture>, except that output is captured as well as passed on to the original STDOUT and STDERR. When called in void context, C<tee> saves memory and time by not reading back from the capture handles, except when the original STDOUT OR STDERR were tied or opened to a scalar handle. =head2 tee_stdout ($stdout, @result) = tee_stdout \&code; $stdout = tee_stdout \&code; The C<tee_stdout> function works just like C<tee> except only STDOUT is teed. STDERR is not teed (output goes to STDERR as usual). =head2 tee_stderr ($stderr, @result) = tee_stderr \&code; $stderr = tee_stderr \&code; The C<tee_stderr> function works just like C<tee> except only STDERR is teed. STDOUT is not teed (output goes to STDOUT as usual). =head2 tee_merged ($merged, @result) = tee_merged \&code; $merged = tee_merged \&code; The C<tee_merged> function works just like C<capture_merged> except that output is captured as well as passed on to STDOUT. Caution: STDOUT and STDERR output in the merged result are not guaranteed to be properly ordered due to buffering. =head1 LIMITATIONS =head2 Portability Portability is a goal, not a guarantee. C<tee> requires fork, except on Windows where C<system(1, @cmd)> is used instead. Not tested on any particularly esoteric platforms yet. See the L<CPAN Testers Matrix|http://matrix.cpantesters.org/?dist=Capture-Tiny> for test result by platform. =head2 PerlIO layers Capture::Tiny does its best to preserve PerlIO layers such as ':utf8' or ':crlf' when capturing (only for Perl 5.8.1+) . Layers should be applied to STDOUT or STDERR I<before> the call to C<capture> or C<tee>. This may not work for tied filehandles (see below). =head2 Modifying filehandles before capturing Generally speaking, you should do little or no manipulation of the standard IO filehandles prior to using Capture::Tiny. In particular, closing, reopening, localizing or tying standard filehandles prior to capture may cause a variety of unexpected, undesirable and/or unreliable behaviors, as described below. Capture::Tiny does its best to compensate for these situations, but the results may not be what you desire. =head3 Closed filehandles Capture::Tiny will work even if STDIN, STDOUT or STDERR have been previously closed. However, since they will be reopened to capture or tee output, any code within the captured block that depends on finding them closed will, of course, not find them to be closed. If they started closed, Capture::Tiny will close them again when the capture block finishes. Note that this reopening will happen even for STDIN or a filehandle not being captured to ensure that the filehandle used for capture is not opened to file descriptor 0, as this causes problems on various platforms. Prior to Perl 5.12, closed STDIN combined with PERL_UNICODE=D leaks filehandles and also breaks tee() for undiagnosed reasons. So don't do that. =head3 Localized filehandles If code localizes any of Perl's standard filehandles before capturing, the capture will affect the localized filehandles and not the original ones. External system calls are not affected by localizing a filehandle in Perl and will continue to send output to the original filehandles (which will thus not be captured). =head3 Scalar filehandles If STDOUT or STDERR are reopened to scalar filehandles prior to the call to C<capture> or C<tee>, then Capture::Tiny will override the output filehandle for the duration of the C<capture> or C<tee> call and then, for C<tee>, send captured output to the output filehandle after the capture is complete. (Requires Perl 5.8) Capture::Tiny attempts to preserve the semantics of STDIN opened to a scalar reference, but note that external processes will not be able to read from such a handle. Capture::Tiny tries to ensure that external processes will read from the null device instead, but this is not guaranteed. =head3 Tied output filehandles If STDOUT or STDERR are tied prior to the call to C<capture> or C<tee>, then Capture::Tiny will attempt to override the tie for the duration of the C<capture> or C<tee> call and then send captured output to the tied filehandle after the capture is complete. (Requires Perl 5.8) Capture::Tiny may not succeed resending UTF-8 encoded data to a tied STDOUT or STDERR filehandle. Characters may appear as bytes. If the tied filehandle is based on L<Tie::StdHandle>, then Capture::Tiny will attempt to determine appropriate layers like C<:utf8> from the underlying filehandle and do the right thing. =head3 Tied input filehandle Capture::Tiny attempts to preserve the semantics of tied STDIN, but this requires Perl 5.8 and is not entirely predictable. External processes will not be able to read from such a handle. Unless having STDIN tied is crucial, it may be safest to localize STDIN when capturing: my ($out, $err) = do { local *STDIN; capture { ... } }; =head2 Modifying filehandles during a capture Attempting to modify STDIN, STDOUT or STDERR I<during> C<capture> or C<tee> is almost certainly going to cause problems. Don't do that. =head3 Forking inside a capture Forks aren't portable. The behavior of filehandles during a fork is even less so. If Capture::Tiny detects that a fork has occurred within a capture, it will shortcut in the child process and return empty strings for captures. Other problems may occur in the child or parent, as well. Forking in a capture block is not recommended. =head3 Using threads Filehandles are global. Mixing up I/O and captures in different threads without coordination is going to cause problems. Besides, threads are officially discouraged. =head3 Dropping privileges during a capture If you drop privileges during a capture, temporary files created to facilitate the capture may not be cleaned up afterwards. =head2 No support for Perl 5.8.0 It's just too buggy when it comes to layers and UTF-8. Perl 5.8.1 or later is recommended. =head2 Limited support for Perl 5.6 Perl 5.6 predates PerlIO. UTF-8 data may not be captured correctly. =head1 ENVIRONMENT =head2 PERL_CAPTURE_TINY_TIMEOUT Capture::Tiny uses subprocesses internally for C<tee>. By default, Capture::Tiny will timeout with an error if such subprocesses are not ready to receive data within 30 seconds (or whatever is the value of C<$Capture::Tiny::TIMEOUT>). An alternate timeout may be specified by setting the C<PERL_CAPTURE_TINY_TIMEOUT> environment variable. Setting it to zero will disable timeouts. B<NOTE>, this does not timeout the code reference being captured -- this only prevents Capture::Tiny itself from hanging your process waiting for its child processes to be ready to proceed. =head1 SEE ALSO This module was inspired by L<IO::CaptureOutput>, which provides similar functionality without the ability to tee output and with more complicated code and API. L<IO::CaptureOutput> does not handle layers or most of the unusual cases described in the L</Limitations> section and I no longer recommend it. There are many other CPAN modules that provide some sort of output capture, albeit with various limitations that make them appropriate only in particular circumstances. I'm probably missing some. The long list is provided to show why I felt Capture::Tiny was necessary. =over 4 =item * L<IO::Capture> =item * L<IO::Capture::Extended> =item * L<IO::CaptureOutput> =item * L<IPC::Capture> =item * L<IPC::Cmd> =item * L<IPC::Open2> =item * L<IPC::Open3> =item * L<IPC::Open3::Simple> =item * L<IPC::Open3::Utils> =item * L<IPC::Run> =item * L<IPC::Run::SafeHandles> =item * L<IPC::Run::Simple> =item * L<IPC::Run3> =item * L<IPC::System::Simple> =item * L<Tee> =item * L<IO::Tee> =item * L<File::Tee> =item * L<Filter::Handle> =item * L<Tie::STDERR> =item * L<Tie::STDOUT> =item * L<Test::Output> =back =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan =head1 SUPPORT =head2 Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at L<https://github.com/dagolden/Capture-Tiny/issues>. You will be notified automatically of any progress on your issue. =head2 Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. L<https://github.com/dagolden/Capture-Tiny> git clone https://github.com/dagolden/Capture-Tiny.git =head1 AUTHOR David Golden <dagolden@cpan.org> =head1 CONTRIBUTORS =for stopwords Dagfinn Ilmari Mannsåker David E. Wheeler fecundf Graham Knop Peter Rabbitson =over 4 =item * Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> =item * David E. Wheeler <david@justatheory.com> =item * fecundf <not.com+github@gmail.com> =item * Graham Knop <haarg@haarg.org> =item * Peter Rabbitson <ribasushi@cpan.org> =back =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2009 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 =cut MYMETA.json 0000644 00000006522 15043642076 0006447 0 ustar 00 { "abstract" : "Capture STDOUT and STDERR from Perl, XS or external programs", "author" : [ "David Golden <dagolden@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010", "license" : [ "apache_2_0" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Capture-Tiny", "no_index" : { "directory" : [ "corpus", "examples", "t", "xt" ], "package" : [ "DB" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.17" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::OSPrereqs" : "0", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::Plugin::ReleaseStatus::FromVersion" : "0", "Dist::Zilla::Plugin::RemovePrereqs" : "0", "Dist::Zilla::PluginBundle::DAGOLDEN" : "0.072", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Pod::Coverage::TrustPod" : "0", "Pod::Wordlist" : "0", "Software::License::Apache_2_0" : "0", "Test::CPAN::Meta" : "0", "Test::MinimumVersion" : "0", "Test::More" : "0", "Test::Perl::Critic" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Portability::Files" : "0", "Test::Spelling" : "0.12", "Test::Version" : "1" } }, "runtime" : { "requires" : { "Carp" : "0", "Exporter" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "Scalar::Util" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "IO::File" : "0", "Test::More" : "0.62", "lib" : "0" } } }, "provides" : { "Capture::Tiny" : { "file" : "lib/Capture/Tiny.pm", "version" : "0.48" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/dagolden/Capture-Tiny/issues" }, "homepage" : "https://github.com/dagolden/Capture-Tiny", "repository" : { "type" : "git", "url" : "https://github.com/dagolden/Capture-Tiny.git", "web" : "https://github.com/dagolden/Capture-Tiny" } }, "version" : "0.48", "x_authority" : "cpan:DAGOLDEN", "x_contributors" : [ "Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>", "David E. Wheeler <david@justatheory.com>", "fecundf <not.com+github@gmail.com>", "Graham Knop <haarg@haarg.org>", "Peter Rabbitson <ribasushi@cpan.org>" ], "x_generated_by_perl" : "v5.26.1", "x_serialization_backend" : "JSON::PP version 4.06" } README 0000644 00000032177 15043642076 0005445 0 ustar 00 NAME Capture::Tiny - Capture STDOUT and STDERR from Perl, XS or external programs VERSION version 0.48 SYNOPSIS use Capture::Tiny ':all'; # capture from external command ($stdout, $stderr, $exit) = capture { system( $cmd, @args ); }; # capture from arbitrary code (Perl or external) ($stdout, $stderr, @result) = capture { # your code here }; # capture partial or merged output $stdout = capture_stdout { ... }; $stderr = capture_stderr { ... }; $merged = capture_merged { ... }; # tee output ($stdout, $stderr) = tee { # your code here }; $stdout = tee_stdout { ... }; $stderr = tee_stderr { ... }; $merged = tee_merged { ... }; DESCRIPTION Capture::Tiny provides a simple, portable way to capture almost anything sent to STDOUT or STDERR, regardless of whether it comes from Perl, from XS code or from an external program. Optionally, output can be teed so that it is captured while being passed through to the original filehandles. Yes, it even works on Windows (usually). Stop guessing which of a dozen capturing modules to use in any particular situation and just use this one. USAGE The following functions are available. None are exported by default. capture ($stdout, $stderr, @result) = capture \&code; $stdout = capture \&code; The "capture" function takes a code reference and returns what is sent to STDOUT and STDERR as well as any return values from the code reference. In scalar context, it returns only STDOUT. If no output was received for a filehandle, it returns an empty string for that filehandle. Regardless of calling context, all output is captured -- nothing is passed to the existing filehandles. It is prototyped to take a subroutine reference as an argument. Thus, it can be called in block form: ($stdout, $stderr) = capture { # your code here ... }; Note that the coderef is evaluated in list context. If you wish to force scalar context on the return value, you must use the "scalar" keyword. ($stdout, $stderr, $count) = capture { my @list = qw/one two three/; return scalar @list; # $count will be 3 }; Also note that within the coderef, the @_ variable will be empty. So don't use arguments from a surrounding subroutine without copying them to an array first: sub wont_work { my ($stdout, $stderr) = capture { do_stuff( @_ ) }; # WRONG ... } sub will_work { my @args = @_; my ($stdout, $stderr) = capture { do_stuff( @args ) }; # RIGHT ... } Captures are normally done to an anonymous temporary filehandle. To capture via a named file (e.g. to externally monitor a long-running capture), provide custom filehandles as a trailing list of option pairs: my $out_fh = IO::File->new("out.txt", "w+"); my $err_fh = IO::File->new("out.txt", "w+"); capture { ... } stdout => $out_fh, stderr => $err_fh; The filehandles must be read/write and seekable. Modifying the files or filehandles during a capture operation will give unpredictable results. Existing IO layers on them may be changed by the capture. When called in void context, "capture" saves memory and time by not reading back from the capture handles. capture_stdout ($stdout, @result) = capture_stdout \&code; $stdout = capture_stdout \&code; The "capture_stdout" function works just like "capture" except only STDOUT is captured. STDERR is not captured. capture_stderr ($stderr, @result) = capture_stderr \&code; $stderr = capture_stderr \&code; The "capture_stderr" function works just like "capture" except only STDERR is captured. STDOUT is not captured. capture_merged ($merged, @result) = capture_merged \&code; $merged = capture_merged \&code; The "capture_merged" function works just like "capture" except STDOUT and STDERR are merged. (Technically, STDERR is redirected to the same capturing handle as STDOUT before executing the function.) Caution: STDOUT and STDERR output in the merged result are not guaranteed to be properly ordered due to buffering. tee ($stdout, $stderr, @result) = tee \&code; $stdout = tee \&code; The "tee" function works just like "capture", except that output is captured as well as passed on to the original STDOUT and STDERR. When called in void context, "tee" saves memory and time by not reading back from the capture handles, except when the original STDOUT OR STDERR were tied or opened to a scalar handle. tee_stdout ($stdout, @result) = tee_stdout \&code; $stdout = tee_stdout \&code; The "tee_stdout" function works just like "tee" except only STDOUT is teed. STDERR is not teed (output goes to STDERR as usual). tee_stderr ($stderr, @result) = tee_stderr \&code; $stderr = tee_stderr \&code; The "tee_stderr" function works just like "tee" except only STDERR is teed. STDOUT is not teed (output goes to STDOUT as usual). tee_merged ($merged, @result) = tee_merged \&code; $merged = tee_merged \&code; The "tee_merged" function works just like "capture_merged" except that output is captured as well as passed on to STDOUT. Caution: STDOUT and STDERR output in the merged result are not guaranteed to be properly ordered due to buffering. LIMITATIONS Portability Portability is a goal, not a guarantee. "tee" requires fork, except on Windows where "system(1, @cmd)" is used instead. Not tested on any particularly esoteric platforms yet. See the CPAN Testers Matrix <http://matrix.cpantesters.org/?dist=Capture-Tiny> for test result by platform. PerlIO layers Capture::Tiny does its best to preserve PerlIO layers such as ':utf8' or ':crlf' when capturing (only for Perl 5.8.1+) . Layers should be applied to STDOUT or STDERR *before* the call to "capture" or "tee". This may not work for tied filehandles (see below). Modifying filehandles before capturing Generally speaking, you should do little or no manipulation of the standard IO filehandles prior to using Capture::Tiny. In particular, closing, reopening, localizing or tying standard filehandles prior to capture may cause a variety of unexpected, undesirable and/or unreliable behaviors, as described below. Capture::Tiny does its best to compensate for these situations, but the results may not be what you desire. Closed filehandles Capture::Tiny will work even if STDIN, STDOUT or STDERR have been previously closed. However, since they will be reopened to capture or tee output, any code within the captured block that depends on finding them closed will, of course, not find them to be closed. If they started closed, Capture::Tiny will close them again when the capture block finishes. Note that this reopening will happen even for STDIN or a filehandle not being captured to ensure that the filehandle used for capture is not opened to file descriptor 0, as this causes problems on various platforms. Prior to Perl 5.12, closed STDIN combined with PERL_UNICODE=D leaks filehandles and also breaks tee() for undiagnosed reasons. So don't do that. Localized filehandles If code localizes any of Perl's standard filehandles before capturing, the capture will affect the localized filehandles and not the original ones. External system calls are not affected by localizing a filehandle in Perl and will continue to send output to the original filehandles (which will thus not be captured). Scalar filehandles If STDOUT or STDERR are reopened to scalar filehandles prior to the call to "capture" or "tee", then Capture::Tiny will override the output filehandle for the duration of the "capture" or "tee" call and then, for "tee", send captured output to the output filehandle after the capture is complete. (Requires Perl 5.8) Capture::Tiny attempts to preserve the semantics of STDIN opened to a scalar reference, but note that external processes will not be able to read from such a handle. Capture::Tiny tries to ensure that external processes will read from the null device instead, but this is not guaranteed. Tied output filehandles If STDOUT or STDERR are tied prior to the call to "capture" or "tee", then Capture::Tiny will attempt to override the tie for the duration of the "capture" or "tee" call and then send captured output to the tied filehandle after the capture is complete. (Requires Perl 5.8) Capture::Tiny may not succeed resending UTF-8 encoded data to a tied STDOUT or STDERR filehandle. Characters may appear as bytes. If the tied filehandle is based on Tie::StdHandle, then Capture::Tiny will attempt to determine appropriate layers like ":utf8" from the underlying filehandle and do the right thing. Tied input filehandle Capture::Tiny attempts to preserve the semantics of tied STDIN, but this requires Perl 5.8 and is not entirely predictable. External processes will not be able to read from such a handle. Unless having STDIN tied is crucial, it may be safest to localize STDIN when capturing: my ($out, $err) = do { local *STDIN; capture { ... } }; Modifying filehandles during a capture Attempting to modify STDIN, STDOUT or STDERR *during* "capture" or "tee" is almost certainly going to cause problems. Don't do that. Forking inside a capture Forks aren't portable. The behavior of filehandles during a fork is even less so. If Capture::Tiny detects that a fork has occurred within a capture, it will shortcut in the child process and return empty strings for captures. Other problems may occur in the child or parent, as well. Forking in a capture block is not recommended. Using threads Filehandles are global. Mixing up I/O and captures in different threads without coordination is going to cause problems. Besides, threads are officially discouraged. Dropping privileges during a capture If you drop privileges during a capture, temporary files created to facilitate the capture may not be cleaned up afterwards. No support for Perl 5.8.0 It's just too buggy when it comes to layers and UTF-8. Perl 5.8.1 or later is recommended. Limited support for Perl 5.6 Perl 5.6 predates PerlIO. UTF-8 data may not be captured correctly. ENVIRONMENT PERL_CAPTURE_TINY_TIMEOUT Capture::Tiny uses subprocesses internally for "tee". By default, Capture::Tiny will timeout with an error if such subprocesses are not ready to receive data within 30 seconds (or whatever is the value of $Capture::Tiny::TIMEOUT). An alternate timeout may be specified by setting the "PERL_CAPTURE_TINY_TIMEOUT" environment variable. Setting it to zero will disable timeouts. NOTE, this does not timeout the code reference being captured -- this only prevents Capture::Tiny itself from hanging your process waiting for its child processes to be ready to proceed. SEE ALSO This module was inspired by IO::CaptureOutput, which provides similar functionality without the ability to tee output and with more complicated code and API. IO::CaptureOutput does not handle layers or most of the unusual cases described in the "Limitations" section and I no longer recommend it. There are many other CPAN modules that provide some sort of output capture, albeit with various limitations that make them appropriate only in particular circumstances. I'm probably missing some. The long list is provided to show why I felt Capture::Tiny was necessary. * IO::Capture * IO::Capture::Extended * IO::CaptureOutput * IPC::Capture * IPC::Cmd * IPC::Open2 * IPC::Open3 * IPC::Open3::Simple * IPC::Open3::Utils * IPC::Run * IPC::Run::SafeHandles * IPC::Run::Simple * IPC::Run3 * IPC::System::Simple * Tee * IO::Tee * File::Tee * Filter::Handle * Tie::STDERR * Tie::STDOUT * Test::Output SUPPORT Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at <https://github.com/dagolden/Capture-Tiny/issues>. You will be notified automatically of any progress on your issue. Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. <https://github.com/dagolden/Capture-Tiny> git clone https://github.com/dagolden/Capture-Tiny.git AUTHOR David Golden <dagolden@cpan.org> CONTRIBUTORS * Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> * David E. Wheeler <david@justatheory.com> * fecundf <not.com+github@gmail.com> * Graham Knop <haarg@haarg.org> * Peter Rabbitson <ribasushi@cpan.org> COPYRIGHT AND LICENSE This software is Copyright (c) 2009 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004
| ver. 1.4 |
Github
|
.
| PHP 8.2.29 | Генераци� �траницы: 0 |
proxy
|
phpinfo
|
�а�тройка