You are here

Programming

Playing with rrdtool counter resets

Not so frequently asked questions and stuff: 

Creating and plotting some data normally

Let's create a standard rdd file, with a counter:

rrdtool create test.rrd --start $start --step 60 DS:something:COUNTER:600:0:U RRA:AVERAGE:0.5:1:1440

Now let's update it with some values.

#!/usr/bin/env perl -w

use strict;
use warnings;

my $start = 1417392000;
my $counter = 0;

for my $x (1..60*24) {
    my $time = $start + 60*$x;
    $counter += 1000+ int(250*sin(1/(48)*$x));
    my $command = "rrdtool update test.rrd $time:$counter";
    system($command);
}

Let's graph!

rrdtool graph test.png -a PNG --start 1417392000 --end 1417478400 -t "Sure looks like a sine" -v "avg potato per second" \
    DEF:something=test.rrd:something:AVERAGE \
    LINE1:something#111111:"My superb value"

Image

So far, so good.

Having a counter reset

Now, let's recreate the same data, but with a counter reset in the middle.

    if ($x == 1000) {
        $counter = 0;
    }

The dreaded spike appears!

Image

Folder http://oss.oetiker.ch/rrdtool/pub/contrib/ contains many tools to remove spikes from rrd data files. My favorite one is spikekill (spikekill-1.1-1.tar.gz).

php removespikes.php -d -A=avg -M=variance -P=5000 -R=test.rrd

The spike is then replaced by a small innocent glitch.

Image

To prevent counter reset spikes, you should use DERIVE instead of COUNTER. Tuning the rrd file solves the problem.

rrdtool tune test.rrd --data-source-type something:DERIVE

How to write a table into a PDF using Python and Reportlab

Not so frequently asked questions and stuff: 

Image

This code will output a table into a lanscaped A4 PDF, with word wrap enabled.

#!/usr/local/bin/python

from reportlab.lib import colors
from reportlab.lib.pagesizes import A4, inch, landscape
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet

doc = SimpleDocTemplate("test_report_lab.pdf", pagesize=A4, rightMargin=30,leftMargin=30, topMargin=30,bottomMargin=18)
doc.pagesize = landscape(A4)
elements = []

data = [
["Letter", "Number", "Stuff", "Long stuff that should be wrapped"],
["A", "01", "ABCD", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"],
["B", "02", "CDEF", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"],
["C", "03", "SDFSDF", "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"],
["D", "04", "SDFSDF", "DDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"],
["E", "05", "GHJGHJGHJ", "EEEEEEEEEEEEEE EEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEE"],
]

#TODO: Get this line right instead of just copying it from the docs
style = TableStyle([('ALIGN',(1,1),(-2,-2),'RIGHT'),
                       ('TEXTCOLOR',(1,1),(-2,-2),colors.red),
                       ('VALIGN',(0,0),(0,-1),'TOP'),
                       ('TEXTCOLOR',(0,0),(0,-1),colors.blue),
                       ('ALIGN',(0,-1),(-1,-1),'CENTER'),
                       ('VALIGN',(0,-1),(-1,-1),'MIDDLE'),
                       ('TEXTCOLOR',(0,-1),(-1,-1),colors.green),
                       ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
                       ('BOX', (0,0), (-1,-1), 0.25, colors.black),
                       ])

#Configure style and word wrap
s = getSampleStyleSheet()
s = s["BodyText"]
s.wordWrap = 'CJK'
data2 = [[Paragraph(cell, s) for cell in row] for row in data]
t=Table(data2)
t.setStyle(style)

#Send the data and build the file
elements.append(t)
doc.build(elements)

Here's the output.
Image

If you use the default word wrap configuration, words that are too long won't be wrapped:

s.wordWrap = 'LTR'

Image

How to apply FreeBSD10's autotools patches manually

Not so frequently asked questions and stuff: 

The FreeBSD logoImage

Situation

You're manually building something that require autotools on FreeBSD 10.

You're used to the port system patching your autotools files, and you want to apply theses patches to your current folder.

Script

Choose your poison:

/bin/tcsh

foreach f (`find . -type f \( -name config.libpath -o -name config.rpath -o -name configure -o -name libtool.m4 -o -name ltconfig -o -name libtool -o -name aclocal.m4 -o -name acinclude.m4 \)`)
    sed -i.fbsd10bak -e 's|freebsd1\*)|freebsd1.\*)|g' -e 's|freebsd\[12\]\*)|freebsd[12].*)|g' -e 's|freebsd\[123\]\*)|freebsd[123].*)|g' -e 's|freebsd\[\[12\]\]\*)|freebsd[[12]].*)|g' -e 's|freebsd\[\[123\]\]\*)|freebsd[[123]].*)|g' $f
    touch -f -mr $f.fbsd10bak $f
    rm $f.fbsd10bak
end

/bin/sh:

for f in `find . -type f \( -name config.libpath -o -name config.rpath -o -name configure -o -name libtool.m4 -o -name ltconfig -o -name libtool -o -name aclocal.m4 -o -name acinclude.m4 \)`; do
    sed -i.fbsd10bak -e 's|freebsd1\*)|freebsd1.\*)|g' -e 's|freebsd\[12\]\*)|freebsd[12].*)|g' -e 's|freebsd\[123\]\*)|freebsd[123].*)|g' -e 's|freebsd\[\[12\]\]\*)|freebsd[[12]].*)|g' -e 's|freebsd\[\[123\]\]\*)|freebsd[[123]].*)|g' $f
    touch -f -mr $f.fbsd10bak $f
    rm $f.fbsd10bak
done

How to install pecl-apd with PHP 5.5 on FreeBSD

Not so frequently asked questions and stuff: 

Image

This was written on 2014-10-05.

Fetch source (revision was 297236 at the time).

# svn co http://svn.php.net/repository/pecl/apd/trunk

Apply the following patch:

diff --git a/php_apd.c b/php_apd.c
index 9d7f238..3b1fe7e 100644
--- a/php_apd.c
+++ b/php_apd.c
@@ -56,11 +56,19 @@
 #undef TRACE_ZEND_COMPILE /* define to trace all calls to zend_compile_file */
 ZEND_DLEXPORT zend_op_array* apd_compile_file(zend_file_handle* TSRMLS_DC);
 ZEND_DLEXPORT zend_op_array* (*old_compile_file)(zend_file_handle* TSRMLS_DC);
+#if PHP_VERSION_ID = 20020731
 ZEND_DLEXPORT void apd_execute_internal(zend_execute_data *execute_data_ptr, int return_value_used TSRMLS_DC);
 #endif
 ZEND_DLEXPORT void (*old_execute)(zend_op_array *op_array TSRMLS_DC);
+#else
+ZEND_DLEXPORT void apd_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
+#if  ZEND_EXTENSION_API_NO >= 20020731
+ZEND_DLEXPORT void apd_execute_internal(zend_execute_data *execute_data_ptr, struct _zend_fcall_info *fci, int return_value_used TSRMLS_DC);
+#endif
+ZEND_DLEXPORT void (*old_execute_ex)(zend_execute_data *execute_data TSRMLS_DC);
+#endif
 
 ZEND_DLEXPORT void onStatement(zend_op_array *op_array);
 ZEND_DECLARE_MODULE_GLOBALS(apd);
@@ -337,7 +345,7 @@ char *apd_get_active_function_name(zend_op_array *op_array TSRMLS_DC)
            }
        } 
        else {
-           switch (execd->opline->op2.u.constant.value.lval) {
+           switch (execd->opline->extended_value) {
            case ZEND_EVAL:
                funcname = estrdup("eval");
                break;
@@ -441,8 +449,8 @@ static void log_time(TSRMLS_D)
        if(utime || stime || rtime) {
             int lineno = 0;
             if(EG(active_op_array)) {
-               if(EG(active_op_array)->start_op) {
-                   lineno = EG(active_op_array)->start_op->lineno;
+               if(EG(start_op)) {
+                   lineno = EG(start_op)->lineno;
                } else if(EG(active_op_array)->opcodes) {
                    lineno = EG(active_op_array)->opcodes->lineno;
                }
@@ -510,28 +518,46 @@ PHP_MINIT_FUNCTION(apd)
 {
    ZEND_INIT_MODULE_GLOBALS(apd, php_apd_init_globals, NULL)
    REGISTER_INI_ENTRIES();
+
+#if PHP_VERSION_ID op_array;
+#endif
    char *fname = NULL;
    
    fname = apd_get_active_function_name(op_array TSRMLS_CC);
        trace_function_entry(EG(function_table), fname, ZEND_USER_FUNCTION,
                        zend_get_executed_filename(TSRMLS_C),
                        EG(in_execution)?zend_get_executed_lineno(TSRMLS_C):0);
-       old_execute(op_array TSRMLS_CC);
+#if PHP_VERSION_ID 

If you don't, you'll have the following error output:
 cc -I. -I/root/apdgit/apd2/trunk -DPHP_ATOM_INC -I/root/apdgit/apd2/trunk/include -I/root/apdgit/apd2/trunk/main -I/root/apdgit/apd2/trunk -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /root/apdgit/apd2/trunk/php_apd.c  -fPIC -DPIC -o .libs/php_apd.o
/root/apdgit/apd2/trunk/php_apd.c:206:36: warning: format string is not a string literal (potentially insecure)
      [-Wformat-security]
                fprintf(APD_GLOBALS(pprof_file), newStr);
                                                 ^~~~~~
/root/apdgit/apd2/trunk/php_apd.c:318:12: warning: assigning to 'char *' from 'const char *' discards qualifiers
      [-Wincompatible-pointer-types-discards-qualifiers]
                tmpfname = execd->function_state.function->common.function_name;
                         ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:322:15: warning: assigning to 'char *' from 'const char *' discards qualifiers
      [-Wincompatible-pointer-types-discards-qualifiers]
                                classname = Z_OBJCE(*execd->object)->name;
                                          ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:329:15: warning: assigning to 'char *' from 'const char *' discards qualifiers
      [-Wincompatible-pointer-types-discards-qualifiers]
                                classname = execd->function_state.function->common.scope->name;
                                          ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:340:31: error: no member named 'u' in 'union _znode_op'
                        switch (execd->opline->op2.u.constant.value.lval) {
                                ~~~~~~~~~~~~~~~~~~ ^
/root/apdgit/apd2/trunk/php_apd.c:444:29: error: no member named 'start_op' in 'struct _zend_op_array'
                                if(EG(active_op_array)->start_op) {
                                   ~~~~~~~~~~~~~~~~~~~  ^
/root/apdgit/apd2/trunk/php_apd.c:445:36: error: no member named 'start_op' in 'struct _zend_op_array'
                                        lineno = EG(active_op_array)->start_op->lineno;
                                                 ~~~~~~~~~~~~~~~~~~~  ^
/root/apdgit/apd2/trunk/php_apd.c:506:35: warning: 'memset' call operates on objects of type 'zend_apd_globals'
      (aka 'struct _zend_apd_globals') while the size is based on a different type 'zend_apd_globals *'
      (aka 'struct _zend_apd_globals *') [-Wsizeof-pointer-memaccess]
    memset(apd_globals, 0, sizeof(apd_globals));
           ~~~~~~~~~~~            ^~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:506:35: note: did you mean to dereference the argument to 'sizeof' (and multiply it
      by the number of elements)?
    memset(apd_globals, 0, sizeof(apd_globals));
                                  ^~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:514:15: error: non-object type 'void (zend_op_array *)' is not assignable
        zend_execute = apd_execute;
        ~~~~~~~~~~~~ ^
/root/apdgit/apd2/trunk/php_apd.c:515:24: warning: incompatible pointer types assigning to 'void
      (*)(zend_execute_data *, struct _zend_fcall_info *, int)' from 'void (zend_execute_data *, int)'
      [-Wincompatible-pointer-types]
        zend_execute_internal = apd_execute_internal;
                              ^ ~~~~~~~~~~~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:544:64: error: too few arguments to function call, expected 3, have 2
        execute_internal(execute_data_ptr, return_value_used TSRMLS_CC);
        ~~~~~~~~~~~~~~~~                                              ^
/usr/local/include/php/Zend/zend_execute.h:62:1: note: 'execute_internal' declared here
ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, struct _zend_fcall_info *fci, int return_...
^
/usr/local/include/php/main/../main/php_config.h:6:19: note: expanded from macro 'ZEND_API'
# define ZEND_API __attribute__ ((visibility("default")))
                  ^
/root/apdgit/apd2/trunk/php_apd.c:724:11: warning: assigning to 'char *' from 'const char *' discards qualifiers
      [-Wincompatible-pointer-types-discards-qualifiers]
        filename = zend_get_executed_filename(TSRMLS_C);
                 ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 warnings and 5 errors generated.
*** Error code 1

Configure and build:

# phpize
# ./configure
# make

Install:

# make install

Load:

zend_extension = "/usr/local/lib/php/20121212/apd.so"
apd.dumpdir="/var/tmp/apd"

Check:

# php -v
PHP 5.5.13 (cli) (built: Jun 11 2014 11:54:41)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2014, by Zend Technologies
    with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans
    with Advanced PHP Debugger (APD) v1.0.2-dev, , by George Schlossnagle

You'll find patched version of the project here:
Github: ZeWaren/pecl-apd (https://github.com/ZeWaren/pecl-apd).

Building an OTP release including Lager with rebar and reltool

Not so frequently asked questions and stuff: 

Image

Building an OTP release including Lager with rebar and reltool.

Create your app the usual way:

rebar create-app appid=testapplager

Add the following to rebar.config:

{lib_dirs,["deps"]}.
{sub_dirs, ["rel"]}.
{deps, [
    {lager, ".*", {
        git, "git://github.com/basho/lager.git", "master"}
    }
]}.
{erl_opts, [{parse_transform, lager_transform}]}.

Add some code to your app, get the dependencies and compile.

rebar get-deps
rebar compile

Create a node configuration

mkdir rel && cd rel
rebar create-node nodeid=testapplager

Set the different paths to the dependencies and your app in reltool.config:

{sys, [
       {lib_dirs, ["../deps"]},
       {erts, [{mod_cond, derived}, {app_file, strip}]},
       {app_file, strip},
       {rel, "testapplager", "1",
        [
         kernel,
         stdlib,
         sasl,
         lager,
         testapplager
        ]},
       {rel, "start_clean", "",
        [
         kernel,
         stdlib
        ]},
       {boot_rel, "testapplager"},
       {profile, embedded},
       {incl_cond, exclude},
       {excl_archive_filters, [".*"]}, %% Do not archive built libs
       {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)",
                           "^erts.*/(doc|info|include|lib|man|src)"]},
       {excl_app_filters, ["\.gitignore"]},
       {app, sasl,   [{incl_cond, include}]},
       {app, stdlib, [{incl_cond, include}]},
       {app, kernel, [{incl_cond, include}]},
       {app, lager, [{incl_cond, include}]},
       {app, testapplager, [{mod_cond, app}, {incl_cond, include}, {lib_dir, ".."}]}
      ]}.

{target_dir, "testapplager"}.

{overlay, [
           {mkdir, "log/sasl"},
           {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
           {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
           {copy, "files/testapplager", "bin/testapplager"},
           {copy, "files/testapplager.cmd", "bin/testapplager.cmd"},
           {copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
           {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
           {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"},
           {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}
          ]}.

Try to build the release:

rebar generate

Failure!

ERROR: generate failed while processing /root/some-sub-folder/testapplager/rel: {'EXIT',{{badmatch,{error,"Application compiler is used in release \"testapplager\" and cannot be excluded"}},
         [{rebar_reltool,generate,2,
                         [{file,"src/rebar_reltool.erl"},{line,50}]},
          {rebar_core,run_modules,4,[{file,"src/rebar_core.erl"},{line,425}]},
          {rebar_core,execute,5,[{file,"src/rebar_core.erl"},{line,359}]},
          {rebar_core,process_dir0,6,[{file,"src/rebar_core.erl"},{line,223}]},
          {rebar_core,process_dir,4,[{file,"src/rebar_core.erl"},{line,130}]},
          {rebar_core,process_each,5,[{file,"src/rebar_core.erl"},{line,296}]},
          {rebar_core,process_dir0,6,[{file,"src/rebar_core.erl"},{line,199}]},
          {rebar_core,process_dir,4,
                      [{file,"src/rebar_core.erl"},{line,130}]}]}}

The problem is that reltool won't include any package by default. This means that lager can't get its dependencies built into the release.

To solve this problem, either change the include condition to:

{incl_cond, derived},

Or manually include the dependencies:

       {app, goldrush, [{incl_cond, include}]},
       {app, syntax_tools, [{incl_cond, include}]},
       {app, compiler, [{incl_cond, include}]},

Then rebuild your release.

How to download only one Android source code project using repo

Not so frequently asked questions and stuff: 

Image

Situation

You want to submit a small patch to an Android source project.

You don't want to download the entire 14GB Android source code.

What you did

Initialize repo.

repo init -u https://android.googlesource.com/platform/manifest
Get https://gerrit.googlesource.com/git-repo
remote: Counting objects: 117, done
remote: Finding sources: 100% (117/117)
remote: Total 2879 (delta 1523), reused 2879 (delta 1523)
Receiving objects: 100% (2879/2879), 2.44 MiB | 696 KiB/s, done.
Resolving deltas: 100% (1523/1523), done.
From https://gerrit.googlesource.com/git-repo
 * [new branch]      maint      -> origin/maint
 * [new branch]      master     -> origin/master
 * [new branch]      stable     -> origin/stable
 * [new tag]         v1.0       -> v1.0
 * [new tag]         v1.0.1     -> v1.0.1
[...]
Get https://android.googlesource.com/platform/manifest
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
curl: (22) The requested URL returned error: 404 Not Found
Server does not provide clone.bundle; ignoring.
remote: Counting objects: 110, done
remote: Finding sources: 100% (110/110)
remote: Total 1345 (delta 288), reused 1345 (delta 288)
Receiving objects: 100% (1345/1345), 1.13 MiB | 479 KiB/s, done.
Resolving deltas: 100% (288/288), done.
From https://android.googlesource.com/platform/manifest
[...]
 * [new tag]         android-sdk-support_r11 -> android-sdk-support_r11

Your Name  [U-dl_muriers_em\ZWM]: Erwan Martin
Your Email [ZWM@dl_muriers_em.(none)]: public@fzwte.net

Your identity is: Erwan Martin 
is this correct [y/N]? y

repo has been initialized in /home/ZWM

Sync the project.

repo sync platform/tools/base
Traceback (most recent call last):
  File "/home/ZWM/.repo/repo/main.py", line 500, in 
    _Main(sys.argv[1:])
  File "/home/ZWM/.repo/repo/main.py", line 476, in _Main
    result = repo._Run(argv) or 0
  File "/home/ZWM/.repo/repo/main.py", line 155, in _Run
    result = cmd.Execute(copts, cargs)
  File "/home/ZWM/.repo/repo/subcmds/sync.py", line 624, in Execute
    submodules_ok=opt.fetch_submodules)
  File "/home/ZWM/.repo/repo/command.py", line 184, in GetProjects
    raise InvalidProjectGroupsError(arg)
error.InvalidProjectGroupsError

InvalidProjectGroupsError? Whaaaaaat!?

Why did that happen

Let's open .repo/repo/command.py and go where the command failed.

        for project in projects:
          if not missing_ok and not project.Exists:
            raise NoSuchProjectError(arg)
          if not project.MatchesGroups(groups):
            raise InvalidProjectGroupsError(arg)

Apparently, a notion of group exists. Let's open the manifest and see in what group our project is.


  [...]
  

notdefault? That doesn't sound good.

What you should have done

Init repo including group tools.

repo init -u https://android.googlesource.com/platform/manifest -g tools
[...]

Sync the project.

repo sync platform/tools/base
Fetching project platform/tools/base
[...]
Fetching projects: 100% (1/1), done.
Checking out files: 100% (5614/5614), done.

Create and checkout your branch.

repo start your-local-branch-name platform/tools/base

Work your magic and upload your changes.

repo upload

Reference

How to build Android APKs on a FreeBSD server

Not so frequently asked questions and stuff: 

Edit: this article is out of date. A new one was written two years later here.

ImageThe FreeBSD logo

Situation

Your team is creating Android apps. Your CI server is running FreeBSD and you'd like Jenkins or something else to be able to build your apps to have nice nightly builds.

You don't care about having adb and you especially don't want to install anything related to X11.

Example

In this example, we'll be building an app using gradle.

Install the build requirements

Install gradle

cd /usr/ports/devel/gradle && make install clean

Install the linux compatibility layer

kldload linux
cd /usr/ports/emulators/linux_base-f10 && make install distclean

Remember, you might need to use brandelf on some parts of the SDK later if they won't execute.

Install bash and link /bin/bash to /usr/local/bin/bash.

cd /usr/ports/shells/bash && make install clean
ln -s /usr/local/bin/bash /bin/bash

Install your favorite flavour of Java.

cd /usr/ports/java/openjdk7 && make install clean

Download and update the Android SDK

Download and extract the Android SDK for Linux.

fetch 'http://dl.google.com/android/android-sdk_r23-linux.tgz'
tar xzf android-sdk_r23-linux.tgz

Go into the tools folder and list the contents of the SDK repositories.

./android list sdk -u
Refresh Sources:
  Fetching https://dl-ssl.google.com/android/repository/addons_list-2.xml
  Validate XML
  Parse XML
  Fetched Add-ons List successfully
  Refresh Sources
  Fetching URL: https://dl-ssl.google.com/android/repository/repository-10.xml
  Validate XML: https://dl-ssl.google.com/android/repository/repository-10.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/repository-10.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/addon.xml
  Validate XML: https://dl-ssl.google.com/android/repository/addon.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/addon.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/addon-6.xml
  Validate XML: https://dl-ssl.google.com/android/repository/addon-6.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/addon-6.xml
  Fetching URL: https://dl-ssl.google.com/glass/gdk/addon.xml
  Validate XML: https://dl-ssl.google.com/glass/gdk/addon.xml
  Parse XML:    https://dl-ssl.google.com/glass/gdk/addon.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/extras/intel/addon.xml
  Validate XML: https://dl-ssl.google.com/android/repository/extras/intel/addon.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/extras/intel/addon.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/sys-img/android/sys-img.xml
  Validate XML: https://dl-ssl.google.com/android/repository/sys-img/android/sys-img.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/sys-img/android/sys-img.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/sys-img/android-wear/sys-img.xml
  Validate XML: https://dl-ssl.google.com/android/repository/sys-img/android-wear/sys-img.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/sys-img/android-wear/sys-img.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/sys-img/android-tv/sys-img.xml
  Validate XML: https://dl-ssl.google.com/android/repository/sys-img/android-tv/sys-img.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/sys-img/android-tv/sys-img.xml
  Fetching URL: https://dl-ssl.google.com/android/repository/sys-img/x86/addon-x86.xml
  Validate XML: https://dl-ssl.google.com/android/repository/sys-img/x86/addon-x86.xml
  Parse XML:    https://dl-ssl.google.com/android/repository/sys-img/x86/addon-x86.xml
Packages available for installation or update: 63
   1- Android SDK Tools, revision 23.0.1
   2- Android SDK Platform-tools, revision 20
   3- Android SDK Build-tools, revision 20
   4- Documentation for Android 'L' Preview SDK, revision 1
   5- SDK Platform Android 4.4W, API 20, revision 1
   6- SDK Platform Android L Preview, revision 1
   7- SDK Platform Android 4.4.2, API 19, revision 3
   8- SDK Platform Android 4.3, API 18, revision 2
   9- SDK Platform Android 4.2.2, API 17, revision 2
  10- SDK Platform Android 4.1.2, API 16, revision 4
  11- SDK Platform Android 4.0.3, API 15, revision 3
  12- SDK Platform Android 4.0, API 14, revision 3
  13- SDK Platform Android 3.2, API 13, revision 1
  14- SDK Platform Android 3.1, API 12, revision 3
  15- SDK Platform Android 3.0, API 11, revision 2
  16- SDK Platform Android 2.3.3, API 10, revision 2
  17- SDK Platform Android 2.2, API 8, revision 3
  18- SDK Platform Android 2.1, API 7, revision 3
  19- SDK Platform Android 1.6, API 4, revision 3
  20- SDK Platform Android 1.5, API 3, revision 4
  21- Samples for SDK API 20, revision 1
  22- Samples for SDK API L Preview, revision 1
  23- Samples for SDK API 19, revision 5
  24- Samples for SDK API 18, revision 1
  25- Samples for SDK API 17, revision 1
  26- Samples for SDK API 16, revision 1
  27- Samples for SDK API 15, revision 2
  28- Samples for SDK API 14, revision 2
  29- Samples for SDK API 13, revision 1
  30- Samples for SDK API 12, revision 1
  31- Samples for SDK API 11, revision 1
  32- Samples for SDK API 10, revision 1
  33- Samples for SDK API 8, revision 1
  34- Samples for SDK API 7, revision 1
  35- Google APIs (x86 System Image), Android API 19, revision 5
  36- Google APIs (ARM System Image), Android API 19, revision 5
  37- Glass Development Kit Preview, Android API 19, revision 8
  38- Google APIs, Android API 18, revision 3
  39- Google APIs, Android API 17, revision 3
  40- Google APIs, Android API 16, revision 3
  41- Google APIs, Android API 15, revision 2
  42- Google APIs, Android API 14, revision 2
  43- Google APIs, Android API 13, revision 1
  44- Google TV Addon, Android API 13, revision 1
  45- Google APIs, Android API 12, revision 1
  46- Google TV Addon, Android API 12, revision 2
  47- Google APIs, Android API 11, revision 1
  48- Google APIs, Android API 10, revision 2
  49- Google APIs, Android API 8, revision 2
  50- Google APIs, Android API 7, revision 1
  51- Google APIs, Android API 4, revision 2
  52- Google APIs, Android API 3, revision 3
  53- Android Support Repository, revision 6
  54- Android Support Library, revision 20
  55- Google Play services for Froyo, revision 12
  56- Google Play services, revision 17
  57- Google Repository, revision 8
  58- Google Play APK Expansion Library, revision 3
  59- Google Play Billing Library, revision 5
  60- Google Play Licensing Library, revision 2
  61- Google USB Driver, revision 10
  62- Google Web Driver, revision 2
  63- Intel x86 Emulator Accelerator (HAXM installer), revision 4

-u means "no gui".

Install what you need.

./android update sdk --filter 1,2,3,5,6,53,54,56 -u
[license stuff]
Installing Archives:
  Preparing to install archives
  Downloading Android SDK Platform-tools, revision 20
  Installing Android SDK Platform-tools, revision 20
  Stopping ADB server failed (code -1).
    Installed Android SDK Platform-tools, revision 2099%)
  Downloading Android SDK Build-tools, revision 20
  Installing Android SDK Build-tools, revision 20
    Installed Android SDK Build-tools, revision 2099%)
  Downloading SDK Platform Android 4.4W, API 20, revision 1
  Installing SDK Platform Android 4.4W, API 20, revision 1
    Installed SDK Platform Android 4.4W, API 20, revision 197%)
  Downloading SDK Platform Android L Preview, revision 1
  Installing SDK Platform Android L Preview, revision 1
    Installed SDK Platform Android L Preview, revision 197%)
  Downloading Android Support Repository, revision 6
  Installing Android Support Repository, revision 6
    Installed Android Support Repository, revision 699%)
  Downloading Android Support Library, revision 20
  Installing Android Support Library, revision 20
    Installed Android Support Library, revision 2093%)
  Downloading Google Play services, revision 17
  Installing Google Play services, revision 17
    Installed Google Play services, revision 1796%)
  Downloading Android SDK Tools, revision 23.0.1
  Installing Android SDK Tools, revision 23.0.1
    Installed Android SDK Tools, revision 23.0.199%)
  Stopping ADB server failed (code -1).
  Unable to run 'adb': Cannot run program "/root/android-sdk-linux/platform-tools/adb": error=2, No such file or directory.
  Starting ADB server failed (code -1).
null  Done. 8 packages installed.

Build your apps!

Edit local.properties and set your SDK location. Alternatively, you can set the environment variable ANDROID_HOME.

sdk.dir=/some/where/android-sdk-linux

Start gradle and see if it complains.

gradle
[some stuff]
Welcome to Gradle 1.12.

To run a build, run gradle  ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

BUILD SUCCESSFUL

Total time: 14.139 secs

If not, build!

gradle assembleRelease
[many things]
14:25:54.743 [LIFECYCLE] [org.gradle.BuildResultLogger]
14:25:54.744 [LIFECYCLE] [org.gradle.BuildResultLogger] BUILD SUCCESSFUL
14:25:54.746 [LIFECYCLE] [org.gradle.BuildResultLogger]
14:25:54.748 [LIFECYCLE] [org.gradle.BuildResultLogger] Total time: 1 mins 56.878 secs
ll Truc/build/outputs/apk/
-rw-r--r--  1 root  wheel  1064222 Jul  1 14:25 Truc-release-unaligned.apk
-rw-r--r--  1 root  wheel  1064495 Jul  1 14:25 Truc-release.apk

You can also use the SDK Manager Plugin for gradle, so that your SDK is updated automatically when you build your project.

Problems I met

gradle won't start

gradle

FAILURE: Build failed with an exception.

* What went wrong:
net.rubygrapefruit.platform.internal.jni.PosixFileFunctions.symlink(Ljava/lang/String;Ljava/lang/String;Lnet/rubygrapefruit/platform/internal/FunctionResult;)V

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

libstdc++.so.6 is missing. Install gcc.

cd /usr/ports/lang/gcc && make install clean

Ambiguous method overloading for method java.io.File

> Ambiguous method overloading for method java.io.File#.
  Cannot resolve which method to invoke for [null, class java.lang.String] due to overlapping prototypes between:
        [class java.lang.String, class java.lang.String]
        [class java.io.File, class java.lang.String]

gradle couldn't find the SDK. Did you set sdk.dir in local.properties?


The SDK downloads packages for Windows instead of Linux

The SDK doesn't recognize FreeBSD, so it downloads packages for Windows. It needs to be patched.

Download the sdk source.

git clone https://android.googlesource.com/platform/tools/base

Apply the following patch to the source:

diff -Naur A/common/src/main/java/com/android/SdkConstants.java B/common/src/main/java/com/android/SdkConstants.java
--- A/common/src/main/java/com/android/SdkConstants.java        2014-07-01 16:44:55.000000000 +0200
+++ B/common/src/main/java/com/android/SdkConstants.java        2014-07-01 19:57:58.000000000 +0200
@@ -596,6 +596,8 @@
             return PLATFORM_WINDOWS;
         } else if (os.startsWith("Linux")) {                //$NON-NLS-1$
             return PLATFORM_LINUX;
+        } else if (os.startsWith("FreeBSD")) {              //$NON-NLS-1$
+            return PLATFORM_LINUX;
         }

         return PLATFORM_UNKNOWN;
diff -Naur A/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchFilter.java B/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchFilter.java
--- A/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchFilter.java      2014-07-01 19:57:30.000000000 +0200
+++ B/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchFilter.java      2014-07-01 19:55:33.000000000 +0200
@@ -210,6 +210,8 @@
             hostOS = HostOs.WINDOWS;
         } else if (os.startsWith("Linux")) {                //$NON-NLS-1$
             hostOS = HostOs.LINUX;
+        } else if (os.startsWith("FreeBSD")) {              //$NON-NLS-1$
+            hostOS = HostOs.LINUX;
         }

         BitSize jvmBits;

Rebuild the patched files.

javac sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchFilter.java -cp "sdklib/src/main/java:common/src/main/java"
javac common/src/main/java/com/android/SdkConstants.java

Replace the files inside the jar.

cd sdklib/src/main/java/ && jar uf /some/where/android-sdk-linux/tools/lib/sdklib.jar com/android/sdklib/internal/repository/archives/ArchFilter.class
cd common/src/main/java && jar uf /some/where/android-sdk-linux/tools/lib/common.jar com/android/SdkConstants.class

See this patch on the Android website: https://android-review.googlesource.com/#/c/100271/.

Profit!

How to run a laravel package's migrations

Not so frequently asked questions and stuff: 

Problem

Let's say you fetched a laravel package using composer and whatever, and you want to run its migration files (to have it create its tables for instance).

# php artisan migrate
Nothing to migrate.

Hum, laravel won't find anything to migrate.

Solution

The trick is to give artisan the path containing the migration files:

# php artisan migrate --path=vendor/vendorname/package-name/src/migrations
Migrated: 2013_11_21_000001_create_something
Migrated: 2013_11_21_000002_create_something_else
Migrated: 2014_03_21_000003_add_stuff

How to sign POST requests with OAuth 1.0 using oauth-signpost and HttpURLConnection

Not so frequently asked questions and stuff: 

Situation

Signpost's documentation states that the library cannot be used along with HttpURLConnection to sign standard POST requests. This is not entirely true.

Method setAdditionalParameters() can be used to add a few parameters to the OAuth base string. The trick is to add them already percent-encoded.

Code sample: signing standard URL-encoded POST requests

package test_oauth;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.Scanner;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.http.HttpParameters;

public class TestOAuth {

 public static void main(String[] args) throws IOException, OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException {
      final String endpointUrl = "http://some-api.example.org/index.php/api/v1/end/point";

        //Create an HttpURLConnection and add some headers
      URL url = new URL(endpointUrl);
     HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
     urlConnection.setRequestProperty("Accept", "application/json");
     urlConnection.setRequestMethod("POST");
     urlConnection.setDoOutput(true);

        //Build the list of parameters
      HttpParameters params =  new HttpParameters();
      params.put("param1", "49607,49608,49609");
      params.put("param2", "1396430420");
     params.put("param3", "coucou");
     
        //Sign the request
      //The key to signing the POST fields is to add them as additional parameters, but already percent-encoded; and also to add the realm header.
        OAuthConsumer dealabsConsumer = new DefaultOAuthConsumer ("5314c6fbe7437", "5314c6fbe7bfd");
        HttpParameters doubleEncodedParams =  new HttpParameters();
        Iterator iter = params.keySet().iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            doubleEncodedParams.put(key, OAuth.percentEncode(params.getFirst(key)));
        }
        doubleEncodedParams.put("realm", endpointUrl);
      dealabsConsumer.setAdditionalParameters(doubleEncodedParams);
        dealabsConsumer.sign(urlConnection);
        
        //Create the POST payload
        StringBuilder sb = new StringBuilder();
        iter = params.keySet().iterator();
        for (int i = 0; iter.hasNext(); i++) {
            String param = iter.next();
            if (i > 0) {
                sb.append("&");
            }
            sb.append(param);
            sb.append("=");
            sb.append(OAuth.percentEncode(params.getFirst(param)));
        }
        
        //Send the payload to the connection
        String formEncoded = sb.toString();;
        OutputStreamWriter outputStreamWriter = null;
        try {
            outputStreamWriter = new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
            outputStreamWriter.write(formEncoded);
        } finally {
            if (outputStreamWriter != null) {
                outputStreamWriter.close();
            }
        }
        
        //Send the request and read the output
       try {
           System.out.println("Response: " + urlConnection.getResponseCode() + " " + urlConnection.getResponseMessage());
          InputStream in = new BufferedInputStream(urlConnection.getInputStream());
           String inputStreamString = new Scanner(in,"UTF-8").useDelimiter("\\A").next();
            System.out.println(inputStreamString);
      }
       finally {
           urlConnection.disconnect();
     }
   }

}

This code was tested on Java 7 on a PC, and on Android.

Code sample: signing a request using a raw JSON (or anything else) payload

Added on 2014-09-10. Asked by someone by email.

package test_oauth;
 
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
 
import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.http.HttpParameters;
 
public class TestOAuth {
 
    public static void main(String[] args) throws IOException, OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException {
        final String endpointUrl = "http://some-api.example.org//api/v1/give/me/some/json";
 
        final String JSONPayload = "{\"this\": \"is\", \"some\": \"JSON\"}";
        
        //Create an HttpURLConnection and add some headers
        URL url = new URL(endpointUrl);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestProperty("Accept", "application/json");
        urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf8");
        urlConnection.setRequestMethod("POST");
        urlConnection.setDoOutput(true);
        
        //Sign the request
        OAuthConsumer dealabsConsumer = new DefaultOAuthConsumer ("5314c6fbe7437", "5314c6fbe7bfd");
        HttpParameters doubleEncodedParams =  new HttpParameters();
        doubleEncodedParams.put("realm", endpointUrl);
        dealabsConsumer.setAdditionalParameters(doubleEncodedParams);
        dealabsConsumer.sign(urlConnection);

        //Send the payload to the connection
        OutputStreamWriter outputStreamWriter = null;
        try {
            outputStreamWriter = new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
            outputStreamWriter.write(JSONPayload);
        } finally {
            if (outputStreamWriter != null) {
                outputStreamWriter.close();
            }
        }
         
        //Send the request and read the output
        try {
            System.out.println("Response: " + urlConnection.getResponseCode() + " " + urlConnection.getResponseMessage());
            InputStream in = new BufferedInputStream(urlConnection.getInputStream());
            String inputStreamString = new Scanner(in,"UTF-8").useDelimiter("\\A").next();
            System.out.println(inputStreamString);
        }
        finally {
            urlConnection.disconnect();
        }
    }
 
}

Code sample: signing a request using a mixed multipart content

Added on 2014-10-23.

When mixing classic text fields and file uploads, the key is to add to the signer only the text fields, and build your multipart request normally.

package test_oauth;
  
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.Scanner;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.http.HttpParameters;
  
public class TestOAuth {
  
    public static void main(String[] args) throws IOException, OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException {
     final String endpointUrl = "http://some-api.example.org/upload/some/file/and/information";
  
        final String BOUNDARY = "ThisShouldBeRandomAndComplicated";
        final String BOUNDARY_SPLIT = "--";

        final String SuperfileName = "filename.bin";
        final String SuperfileContent = "This is the content of the file. It can contains anything and can contains anything.";
        final String SuperfileContentType = "application/octet-stream";
         
        //Create an HttpURLConnection and add some headers
        URL url = new URL(endpointUrl);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestProperty("Accept", "application/json");
        urlConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
        urlConnection.setRequestMethod("POST");
        urlConnection.setDoOutput(true);

        //Build the list of parameters
        HttpParameters params =  new HttpParameters();
        params.put("param1", "49607,49608,49609");
        params.put("param2", "1396430420");
        params.put("param3", "coucou");
        
        //Sign the request
        //The key to signing the POST fields is to add them as additional parameters, but already percent-encoded; and also to add the realm header.
        OAuthConsumer dealabsConsumer = new DefaultOAuthConsumer ("5314c6fbe7437", "5314c6fbe7bfd");
        HttpParameters doubleEncodedParams =  new HttpParameters();
        Iterator iter = params.keySet().iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            doubleEncodedParams.put(key, OAuth.percentEncode(params.getFirst(key)));
        }
        doubleEncodedParams.put("realm", endpointUrl);
        dealabsConsumer.setAdditionalParameters(doubleEncodedParams);
        dealabsConsumer.sign(urlConnection);
 
        //Send the payload to the connection
        PrintWriter OutputStream = null;
        try {
            OutputStream = new PrintWriter(new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8"), true);

            //Send the POST variables
            Iterator iterator = params.keySet().iterator();
         while (iterator.hasNext()) {
                String key = iterator.next();
   
                OutputStream.append(BOUNDARY_SPLIT).append(BOUNDARY).append("\r\n");
              OutputStream.append("Content-Disposition: form-data; name=\"").append(key).append("\"").append("\r\n");
             OutputStream.append("Content-Type: ").append("text/plain; charset=UTF-8").append("\r\n").append("\r\n");
                OutputStream.append(params.getFirst(key)).append("\r\n")
              .flush();
           }
           
            //Send the file
         OutputStream.append(BOUNDARY_SPLIT).append(BOUNDARY).append("\r\n");
          OutputStream.append("Content-Disposition: form-data; name=\"upload_field_name\"; filename=\""+SuperfileName+"\"").append("\r\n");
         OutputStream.append("Content-Type: ").append(SuperfileContentType).append("\r\n").append("\r\n");
           OutputStream.append(SuperfileContent).append("\r\n");

         //Add one final boundary
            OutputStream.append(BOUNDARY_SPLIT).append(BOUNDARY).append(BOUNDARY_SPLIT).append("\r\n");
        }
        finally {
            if (OutputStream != null) {
              OutputStream.close();
            }
        }
        
        //Send the request and read the output
        try {
            System.out.println("Response: " + urlConnection.getResponseCode() + " " + urlConnection.getResponseMessage());
            InputStream in = new BufferedInputStream(urlConnection.getInputStream());
            String inputStreamString = new Scanner(in,"UTF-8").useDelimiter("\\A").next();
            System.out.println(inputStreamString);
        }
        finally {
            urlConnection.disconnect();
        }
    }
  
}

How to change the icon, version or another resource of Windows executables built with PAR::Packer

Not so frequently asked questions and stuff: 

Situation

You're using PAR::Packer to create a nice Windows executable out of one of your perl script.

However, the --info and --icon switch aren't working. You might also want to embed some other resource into your executable.

Trying the usual methods

exe_update.pl

exe_update --icon=jambon.ico jambon.exe

exe_update does not work with PAR::Packer executable. It will destroy your file.

ResHacker

ResHacker.exe -modify       "jambon.exe", "jambon.exe", "jambon.ico", ICONGROUP, WINEXE, 0
ResHacker.exe -delete       "jambon.exe", "jambon.exe", versioninfo,1,
ResHacker.exe -addoverwrite "jambon.exe", "jambon.exe", "jambon\version_info.res", versioninfo,1,

ResHacker will edit the resource of your file correctly. However, the executable will error when launching since its signature has changed. This is not acceptable.

Where the icon and version comes from

When built statically, a pp compiled executable is made of data stored in Static.pm, which is made from Static.in and boot.exe. This boot.exe file is the one that contains the final resources.

The resources used to build this file are located in cpan\build\PAR-Packer-1.015-?????\myldr\winres. Modifying these files and rebuilding boot.exe will allow you to change your final executable resources.

The ugly solution that works

Before packing your perl script, rebuild boot.exe with your custom information.

This can easily be automated using a makefile, like this one:

PERL_DIR = C:\StupidPrograms\strawberryperl\perl
PAR_PACKER_SRC = C:\StupidPrograms\strawberryperl\cpan\build\PAR-Packer-1.015-2TLZDS

all:
   copy /Y medias\jambon.ico $(PAR_PACKER_SRC)\myldr\winres\pp.ico
 copy /Y medias\jambon.rc $(PAR_PACKER_SRC)\myldr\winres\pp.rc
   del $(PAR_PACKER_SRC)\myldr\ppresource.coff
   cd /D $(PAR_PACKER_SRC)\myldr\ && perl Makefile.PL
    cd /D $(PAR_PACKER_SRC)\myldr\ && dmake boot.exe
  cd /D $(PAR_PACKER_SRC)\myldr\ && dmake Static.pm
 attrib -R $(PERL_DIR)\site\lib\PAR\StrippedPARL\Static.pm
  copy /Y $(PAR_PACKER_SRC)\myldr\Static.pm $(PERL_DIR)\site\lib\PAR\StrippedPARL\Static.pm

    pp -o jambon.exe -f PodStrip -f Bleach -f Obfuscate --compress=9 jambon.pl

Your perl installation might be different than mine, so you'll want to adapt the scripts.

This solution is quite ugly, but it does work.

Pages

Subscribe to Programming