Creating a Perl 6 module containing Perl 5 utility scripts in bin/ - raku

Perl 6 script in a Perl 5 module distribution
I can include a Perl 6 script in a Perl 5 module distribution:
# Create a new module
dzil new My::Dist
cd My-Dist/
# Add necessary boilerplate
echo '# ABSTRACT: boilerplate' >> lib/My/Dist.pm
# Create Perl 6 script in bin directory
mkdir bin
echo '#!/usr/bin/env perl6' > bin/hello.p6
echo 'put "Hello world!";' >> bin/hello.p6
# Install module
dzil install
# Test script
hello.p6
# Hello world!
# See that it is actually installed
which hello.p6
# ~/perl5/perlbrew/perls/perl-5.20.1/bin/hello.p6
Perl 5 script in a Perl 6 module distribution
However, I'm having a hard time including Perl 5 scripts in a Perl 6 distribution.
In the module directory is a META6.json file and a subdirectory called bin. In bin is a Perl 5 file called hello.pl.
zef install . runs without error in the top directory. But when trying to run hello.pl, I get an error. Come to find out, a Perl 6 wrapper script had been installed for hello.pl and that is what is giving me the error. If I run the original hello.pl directly, it works fine.
META6.json
{
"perl" : "6.c",
"name" : "TESTING1234",
"license" : "Artistic-2.0",
"version" : "0.0.2",
"auth" : "github:author",
"authors" : ["First Last"],
"description" : "TESTING module creation",
"provides" : {
},
"depends" : [ ],
"test-depends" : [ "Test", "Test::META" ]
}
bin/hello.pl
#!/usr/bin/env perl
use v5.10;
use strict;
use warnings;
say 'Hello world!';
This installs without error, but when I try to run hello.pl, I get the following error:
===SORRY!===
Could not find Perl5 at line 2 in:
/home/username/.perl6
/path/to/perl6/rakudo-star-2017.07/install/share/perl6/site
/path/to/perl6/rakudo-star-2017.07/install/share/perl6/vendor
/path/to/perl6/rakudo-star-2017.07/install/share/perl6
CompUnit::Repository::AbsolutePath<64730416>
CompUnit::Repository::NQP<43359856>
CompUnit::Repository::Perl5<43359896>
which hello.pl from the command line indicated that it was installed in /path/to/perl6/rakudo-star-2017.07/install/share/perl6/site/bin/hello.pl. That file is actually the following code:
/path/to/perl6/rakudo-star-2017.07/install/share/perl6/site/bin/hello.pl
#!/usr/bin/env perl6
sub MAIN(:$name is copy, :$auth, :$ver, *#, *%) {
CompUnit::RepositoryRegistry.run-script("hello.pl", :dist-name<TESTING1234>, :$name, :$auth, :$ver);
}
I filed a Rakudo bug report (https://rt.perl.org/Ticket/Display.html?id=131911), but I'm not totally convinced that there isn't a simple work around.

As an example, I created a simple cat replacement in Perl 5 and created a Perl 6 module that "wrapped" around it (see the GitHub repository for it if you'd like to download the code and try it yourself).
Below are copies of the relevant files. After creating these files, running zef install . installs fine with my Rakudo Star 2017.07 installation. This installs a run_cat executable in your Rakudo bin directory.
It seemed like the secret was to make a Perl 6 module file to wrap the Perl 5 script and a corresponding Perl 6 script to use the Perl 6 module.
Perl 5 script
resources/scripts/cat.pl
#!/bin/env perl
use v5.10;
use strict;
use warnings;
while(<>) {
print;
}
Wrapper scripts
module: lib/catenate.pm6
unit module catenate;
sub cat ($filename) is export {
run('perl',%?RESOURCES<scripts/cat.pl>,$filename);
}
executable: bin/run_cat
#!/bin/env perl6
use catenate;
sub MAIN ($filename) {
cat($filename);
}
Boilerplate and tests
META6.json`
{
"perl" : "6.c",
"name" : "cat",
"license" : "Artistic-2.0",
"version" : "0.0.9",
"auth" : "github:author",
"authors" : ["First Last"],
"description" : "file catenation utility",
"provides" : { "catenate" : "lib/catenate.pm6" },
"test-depends" : [ "Test", "Test::META" ],
"resources" : [ "scripts/cat.pl" ]
}
t/cat.t
#!/bin/env perl6
use Test;
constant GREETING = 'Hello world!';
my $filename = 'test.txt';
spurt($filename, GREETING);
my $p5 = qqx{ resources/scripts/cat.pl $filename };
my $p6 = qqx{ bin/run_cat $filename };
is $p6, $p5, 'wrapped script gives same result as original';
is $p6, GREETING, "output is '{GREETING}' as expected";
unlink $filename;
done-testing;
Thanks #moritz and #ugexe for getting me pointed in the right direction!

Related

How do I loop through files in npm in a way that works on Windows & linux?

I'm trying to run a single command (jshint), on multiple files. My package.json contains
"lint": "jshint *.js **/*.js"
However this fails miserable on Windows. On Windows the syntax to iterate on multiple files is
for %%f in (*.in) do (
echo %%~nf
)
Is there a simple, platform-agnostic way to run a single npm script (e.g. jshint) on multiple files?
(I'm interested in the general solution. There's a references here to using node-jslint instead of jshint, which does support multiple files ... but IMO jshint >> jslint).
I'm also not aware of a platform agnostic way to loop in the shell.
However, a platform agnostic solution to running a single npm-script on multiple files with jshint, as per your example, is to:
Utilize cli-glob to find the .js files.
Pipe the results/paths from the globbing pattern to a custom utility node script.
Then within the node script:
Read the paths piped to stdin using nodes readline module.
Create an Array of each path and subsequently convert that to a String.
Run the jshint executable, (including the String of all paths), using nodes child_process.exec() module.
Whilst this solution is not particularly "simple", the following gists demonstrate this process:
npm-script
"scripts": {
"jshint": "glob \"src/js/**/*.js\" | node .scripts/jshint.js"
},
Note cli-glob, (added to package.json), obtains the paths and pipes them to jshint.js.
jshint.js
#!/usr/bin/env node
'use strict';
var path = require('path');
var readline = require('readline');
var exec = require('child_process').exec;
var rl = readline.createInterface({
input: process.stdin,
output: null,
terminal: false
});
// Normalise path to local jshint executable.
var jshintExec = ['.', 'node_modules', '.bin', 'jshint '].join(path.sep);
var paths = [];
function jshint(paths) {
var command = [jshintExec, paths].join('');
exec(command, function(error, stdout, stderr) {
if (stdout) {
console.log(stdout);
}
if (stderr) {
console.log(stderr);
}
});
}
rl.on('line', function(srcPath) {
paths.push(srcPath);
});
rl.on('close', function() {
jshint(paths.join(' '));
});
Note
Line 16 reading:
var jshintExec = ['.', 'node_modules', '.bin', 'jshint '].join(path.sep);
The script assumes jshint has been installed locally and added to the "devDependencies": {} section of the package.json. I.e. Its pointing to the local jhint executable found in the node_modules/.bin folder and not the globally installed one.
If your preference is to run the globally installed jshint then change line 16 to:
var jshintExec = 'jshint ';
Personally, having it installed locally is the preferred option IMHO for this scenario!
Multiple globbing patterns
Your example provided includes multiple glob patterns.
"lint": "jshint *.js **/*.js"
One limitation of cli-glob is that it doesn't accept multiple glob patterns. So one workaround is to do something like this:
"scripts": {
"jshint": "npm run jshint-a && npm run jshint-b",
"jshint-a": "glob \"*.js\" | node .scripts/jshint.js",
"jshint-b": "glob \"**/*.js\" | node .scripts/jshint.js"
},
Yeah, not particularly terse - but works!
To the best of my knowledge, you can't loop in the shell in a cross-platform way.
However, you can use https://www.npmjs.com/package/catw and do something like this:
catw "**/*.js" | jshint -
catw will expand the glob(s) itself without relying on the shell and write the files to stdout. jshint - will read from stdin. The pipe (|) works cross-platform.

Finding, from cocoa application, if a package installer (like DivXInstaller.pkg) is running

I do not know if this is possible, so I hope that the question does not appear stupid:
I can get in a cocoa application, or even using a bash script, the pid of a specific running Application. So I can take actions (such as alert and ask if you want to close it).
We can the same with a package (pkg), knowing its id (like "org.someIdentity.pkg") in some way?
EDIT
this is what i use in cocoa:
- (void)unWantedApp:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
NSLog(#"userInfo == %#", userInfo);
if ([NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.blabla"] || [NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.blabla2"]) {
[[NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(#"blabla is running..", nil), [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]]
defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(#"Well, I hope you are not joking with more then one Tool at same time...", nil)] runModal];
}
}
EDIT
what i used with bash:
isOpen=$(ps auxwww | grep '/Applications/blabla.app' | grep -v 'grep' | wc -l)
if [ -e '/Applications/blabla.app' ]; then
if [ $isOpen -ge 0 ]; then
# do what you are intresting for... But a packages is not in Application folder and is opened by Installer.app :-(
fi
fi
EDIT
also the bash script can be used in cocoa in this way:
#include <stdio.h>
#include <stdlib.h>
#define SCRIPT "\
#/bin/bash \n\
if [ -e '/Applications/blabla.app' ];then\n\
#variable and other statement here(escape all special caracter and manually add the new line ->> \n)\n\
else\n\
exit 0\n\
fi"
int main()
{
system(SCRIPT);
return 0;
}
Maybe this helps a bit:
app=${1:-VirtualBox} #tested on it
run_searched() {
echo "The installer installing $1 as: $2"
}
run_other() {
echo "The installer NOT installing $1. (but installing: $2)"
}
not_running() {
echo "The installer is not running"
}
isasdir="$HOME/Library/Saved Application State/com.apple.installer.savedState"
isasfile="$isasdir/windows.plist"
if [[ -d "$isasdir" && -f "$isasfile" ]]
then
win=$(plutil -p "$isasfile" | grep '"NSTitle"' | sed 's:.*"NSTitle".*=>.*"\([^"]*\)":\1:')
if [[ "$win" =~ $app ]]
then
run_searched "$app" "$win"
else
run_other "$app" "$win"
fi
else
not_running
fi
I added a comment with some questions, but I will provide some kind of answer too.
As mentioned in my comment: If the applications run with absolute path you can probably look for processes with org.someIdentity
If applications you want to 'monitor' are running from the same path(s), you could probably do something along these lines (bash):
apps="org.someIdentity org.someOtherIdentity"
for app in ${apps}; do
echo "Checking app ${app}..."
lsof /Applications/${app}
done
This is a crude example - you might have to expand on the paths a bit (I'm not on a Mac), but the idea is that the application/package names exist in the file structure (as directories). Running lsof on these directories will tell us which processes are using resources in that directory (i.e. opened files). Depending on your needs you can probably pass lsof some more parameters.
I'm not saying this is a very pretty approach, though ;)
If using OS X 10.8 or later, pgrep is the best tool to return a list of process IDs by application name.
$ pgrep firefox
905

Running DOH on the command line with node.js

My first attempt to run tests using Dojo 1.8.3 on the command line with
node was thwarted by this error message:
../dojo/dojo.js:15
ode:function(){return _25;},guardCheckComplete:_37};};if(1){var _38=location.p
^
ReferenceError: location is not defined
The workaround is to use the uncompressed source instead of the release,
since the optimized version only seems to work in a browser. Next I tried a configuration script
// bootstrap.js
dojoConfig = {
baseUrl: ".",
packages:[{name: 'dojo', location: '../dojo_src'}],
deps: ['./ui_test.js']
};
require('../dojo_src/dojo.js');
And a simple test
// ui_test.js
dojo.require("doh.runner");
doh.register("test_1", [
function four_eq_4() {
var x = 4;
doh.is(x.toString(), "4");
}
]);
doh.run();
console.log("done.");
When I run the tests doh.run() does not seem to have an effect
$ node bootstrap.js
done.
My directory structure:
/app
bootstrap.js
ui_test.js
/util/doh
/dojo_src
What is the correct way to use DOH on the command line?
The answer is simple, but not obvious. Run with load=doh to invoke the test runner.
$ node bootstrap.js load=odh

Using translations of Behat predefined steps (Phar install)

I've run some tests with the predefined step definitions of Mink Extension. They work as long as they're in english language.
Now I've tried the following scenario with german steps:
# language: de
Funktionalität: Demo
#javascript
Szenario: Test 1
Angenommen I am on "/"
Angenommen ich bin auf "/"
...
Behat now tells me that the german step definition is undefined, while the english version works.
According to the CLI help, behat --lang de -dl should display the translated definitions, but it only shows me the english ones ...
What am I doing wrong here?
Edit:
Here's a script to rebuild the scenario. It follows the install steps from the docs (http://extensions.behat.org/mink/#through-phar) in a temporary directory and runs the test feature file.
#!/bin/bash
set -e
TEMPDIR=/tmp/behat-$$
mkdir $TEMPDIR
cd $TEMPDIR
curl http://behat.org/downloads/behat.phar >behat.phar
curl http://behat.org/downloads/mink.phar >mink.phar
curl http://behat.org/downloads/mink_extension.phar >mink_extension.phar
cat >behat.yml <<EOF
default:
extensions:
mink_extension.phar:
mink_loader: 'mink.phar'
base_url: 'http://behat.org'
goutte: ~
EOF
mkdir features
cat >features/test.feature <<EOF
# language: de
Funktionalität: Demo
Szenario: Öffne Startseite DE + EN
Angenommen I am on "/"
Angenommen ich bin auf "/"
EOF
php behat.phar
Basically you didn't do anything wrong.
Although the translation of Behat/Gherkin itself is included in the behat.phar file, the translations of the step definitions from MinkExtension are missing in the mink_extension.phar archive.
This seems to be the case because the build script only includes the files in MinkExtension/src/ without MinkExtension/i18n/. You could open an issue for MinkExtension at to get this fixed.
As a workaround I suggest to install Behat/Mink using composer instead of working with phar archives.
Create the following composer.json file:
{
"require": {
"behat/behat": "2.4.*#stable",
"behat/mink": "1.4.*#stable",
"behat/mink-extension": "*",
"behat/mink-goutte-driver": "*",
"behat/mink-selenium2-driver": "*"
},
"minimum-stability": "dev",
"config": {
"bin-dir": "bin/"
}
}
and then install it with:
curl http://getcomposer.org/installer | php
php composer.phar install

Error6 while trying to use sublime text to msbuild

I'm trying to use msbuild with my sublime project. I created the build file suggested here and the following is my project file
{
"folders":
[
{
"path": "/W/MyOrg/MyApp",
"folder_exclude_patterns": ["_ReSharper.*", "bin", "obj"]
}
]
}
I select the msbuild40 build system and hit Build and get the output:
[Error 6] The handle is invalid
[Finished]
I'm not even sure if this is a python or an msbuild error. Which is it, how can I fix it, and whats a good way to troubleshoot this sort of stuff in the future?
Update
I tried updating my project to the following and using that build and still no dice
{
"folders":
[
{
"path": "/W/MyOrg/MyApp",
"folder_exclude_patterns": ["_ReSharper.*", "bin", "obj"]
}
],
"build_systems":
[
{
"name": "msbuild",
"cmd": ["c:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\MSBuild.exe", "w:\\MyOrg\\MyApp\\MyApp.sln"]
}
]
}
Turns out that this happens whenever you start sublime from command line ( I was starting it via a powershell alias).
You can fix this by using a batch file and the START command. I created sublime_text.bat:
START "Sublime Text 2" "C:\Program Files\Sublime Text 2\sublime_text.exe" %*
and set my powershell alias to that bat file. Now everything works.