1

大量の Tupfile では、大規模なパイプライン処理を使用します。

: input |> < %f command1 | command2 > %o |> output

systemこれに関する問題は、でこれらの :-rules を実行するTup 呼び出しがsh、 をサポートしていないことset -o pipefailです。結果として、command1失敗しただけの場合でも、終了コードが 0 だったので、tup はこれを成功としてマークします。これは非常に問題があります。

私はこれに対する 2 つの解決策を知っていますが、どちらも理想的ではありません。

を。パイプライン処理を放棄して、代わりに次のことを行うことができます。

: input |> < %f command1 > %o |> intermediate
: intermediate |> < %f command2 > %o |> output

これは機能しますが、一連のルールを面倒に書き直す必要があり、さらに重要なことに、更新のたびにディスク容量とディスク書き込みが大幅に多く使用されます。

b) すべてのコマンドを次のbashようにラップできます。

: input |> bash -c 'set -o pipefail && < %f command1 | command2 > %o' |> output

これは、書き換えが少なく、io を回避できるため、わずかに優れているように見えますが、それでも非常に面倒です。また'、:-rules で any をエスケープする必要があります。

理想的には、:-rules を読み取るために使用するシェル/インタープリターを指定するだけの Tup 構成が存在します。理想的には、共通のプレフィックスの構成もあり、すべてのスクリプトを実行したり、他の必要なset -o pipefail &&ものを実行したりできます。私の知る限り、これはすぐには可能ではありません。systemtup がルールを呼び出すたびに、ラッパー アラウンドを記述する必要があります。ただし、提案された 2 つのソリューションよりも洗練されたものを可能にする Tup のいくつかの側面を見落としている可能性があります。

編集:システムへの呼び出しにより、パイプフェイルをシステムへの呼び出しに「注入」することができました。プログラムがシステムを使用して実行されるという事実を誤って述べました。メーリングリストの助けを借りて、それらが実際に を使用して実行されていることが判明しましたexecle。以下は、誰かが同じことを達成したい場合に備えて、介入を行うために使用したコードです。

解決

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>

int execle(const char* path, const char* arg0, ...) {
    /* We're going to interpose this function, modify the arguments if we need
     * to, and then convert it into a call to execve. Due to a weirdness in the
     * consts of the api, we need to discard a const qualifier on the
     * characters in the arguments. The call is `int execve(const char*
     * filename, char* const argv[], char* const envp[]);` but it should
     * probably be `int execve(const char* filename, const char* const argv[],
     * char* const envp[]);` at the very least, e.g. arguments shouldn't be
     * modified. These aren't actually modified by the call, so in order to
     * avoid the inefficiency of copying the strings into memory we don't need,
     * we just do this unsafely and compile with `-Wno-discarded-qualifiers`.
     * */

    // Count the number of variable arguments for malloc
    unsigned int num_args;
    va_list ap;
    va_start(ap, arg0);
    if (arg0) {
        num_args = 1;
        while(va_arg(ap, const char*)) {
            num_args++;
        }
    } else {
        num_args = 0;
    }
    char* const* env = va_arg(ap, char* const*); // Also grab env
    va_end(ap);

    // Test for specific tup execle call
    va_start(ap, arg0);
    int intercept = num_args == 4
        && strcmp(path, "/bin/sh") == 0
        && strcmp(arg0, "/bin/sh") == 0
        && strcmp(va_arg(ap, const char*), "-e") == 0
        && strcmp(va_arg(ap, const char*), "-c") == 0;
    va_end(ap);

    // Switch on whether to intercept the call, or pass it on
    /*const*/ char** args;
    if (intercept) { // We want to switch to bash with pipefail enabled
        args = malloc(7 * sizeof(args));
        path = "/bin/bash";
        args[0] = "/bin/bash";
        args[1] = "-e";
        args[2] = "-o";
        args[3] = "pipefail";
        args[4] = "-c";

        va_start(ap, arg0);
        va_arg(ap, const char*);
        va_arg(ap, const char*);
        args[5] = va_arg(ap, const char*); // command
        va_end(ap);

        args[6] = NULL;

    } else { // Just copy args into a null terminated array for execve
        args = malloc((num_args + 1) * sizeof(*args));

        char** ref = args;
        if (arg0) {
            *ref++ = arg0;
            const char* arg;
            va_start(ap, arg0);
            while ((arg = va_arg(ap, const char*))) {
                *ref++ = arg;
            }
            va_end(ap);
        }
        *ref = NULL;

    }

    int error_code = execve(path, args, env);

    free(args);
    return error_code;
}
4

1 に答える 1

1

あなた自身systemを実装することができます

switch(pid = fork()) {
  case 0:
    // Modify command to prepend "set -o pipefail &&" to it.
    execl("/bin/bash", "bash", "-c", command, (char *) 0);
 case -1: // handle fork error
 default:
    waitpid(pid, ...);
}

そしてLD_PRELOADそのsystem実装をあなたのtupプロセスに。

低レベルのプロセス管理を行いたくない場合は、コマンドをラップして引用符をエスケープしてから、元の を呼び出すことsystemができます。ライブラリの介入に関するこの記事を参照してください。bash -c "set -o pipefail && "system

于 2015-12-04T04:50:24.647 に答える