A build step is a task which exercises one of the build skills.


§1. Build skills. A "skill" is a single atomic task which we know how to perform. For example, assimilating a binary for a kit is a skill.

Each different skill is an instance of:

typedef struct build_skill {
    struct text_stream *name;
    struct method_set *methods;
    CLASS_DEFINITION
} build_skill;

build_skill *BuildSteps::new_skill(text_stream *name) {
    build_skill *S = CREATE(build_skill);
    S->name = Str::duplicate(name);
    S->methods = Methods::new_set();
    return S;
}

§2. Skills provide two method functions: one constructs a shell command to perform the skill, and the other performs the skill directly by calling some function within the current executable. These methods are optional, and if one is absent then the skill can't be performed that way.

enum BUILD_SKILL_COMMAND_MTID
enum BUILD_SKILL_INTERNAL_MTID
VOID_METHOD_TYPE(BUILD_SKILL_COMMAND_MTID,
    build_skill *S, build_step *BS, text_stream *command, build_methodology *meth,
    linked_list *search)
INT_METHOD_TYPE(BUILD_SKILL_INTERNAL_MTID,
    build_skill *S, build_step *BS, build_methodology *meth, linked_list *search)

§3. Build steps. These are essentially just skills, but with a docket of contextual data. The idea is that a function outside the supervisor module can carry out a skill for us using only the contextual information in this structure, without having to access any of inbuild's variables directly.

typedef struct build_step {
    struct build_skill *what_to_do;
    struct build_vertex *vertex;  what to do it to
    struct target_vm *for_vm;
    struct inter_architecture *for_arch;
    int for_release;
    struct inbuild_copy *associated_copy;  e.g., the Inform project causing this work
    CLASS_DEFINITION
} build_step;

§4. We build scripts for a vertex by attaching one step at a time to it:

build_step *BuildSteps::attach(build_vertex *vertex, build_skill *to_do,
    int rel, target_vm *VM, inter_architecture *arch, inbuild_copy *assoc) {
    build_step *S = CREATE(build_step);
    S->what_to_do = to_do;
    S->vertex = vertex;
    S->for_vm = VM;
    S->for_arch = arch;
    if ((VM) && (arch == NULL)) S->for_arch = TargetVMs::get_architecture(VM);
    S->for_release = rel;
    S->associated_copy = assoc;
    BuildScripts::add_step(vertex->script, S);
    return S;
}

§5. Execution. Note that this prints a log of shell commands generated to stdout when we are running inside Inbuild at the command line, but not when we are running inside the inform7 executable, where we are silent throughout.

int BuildSteps::execute(build_vertex *V, build_step *S, build_methodology *BM,
    linked_list *search_list) {
    int rv = TRUE;
    TEMPORARY_TEXT(command)
    Work out a shell command, and perhaps print or call it5.1;
    Perform the skill internally if that's called for5.2;
    #ifndef CORE_MODULE
    if (rv == FALSE) WRITE_TO(STDERR, "Build failed at '%S'\n", command);
    #endif
    DISCARD_TEXT(command)
    return rv;
}

§5.1. Work out a shell command, and perhaps print or call it5.1 =

    VOID_METHOD_CALL(S->what_to_do, BUILD_SKILL_COMMAND_MTID, S, command, BM, search_list);
    if (Str::len(command) > 0) rv = BuildSteps::shell(command, BM);

§5.2. Perform the skill internally if that's called for5.2 =

    if (BM->methodology == INTERNAL_METHODOLOGY) {
        int returned = 0;
        INT_METHOD_CALL(returned, S->what_to_do, BUILD_SKILL_INTERNAL_MTID, S, BM, search_list);
        if (returned != TRUE) rv = FALSE;
    }

§6. This prints a shell command to stdout (except when inside inform7) and also executes it if the methodology allows, returning the result. Note that shell commands return 0 to indicate happiness.

int BuildSteps::shell(text_stream *command, build_methodology *BM) {
    int rv = TRUE;
    #ifndef CORE_MODULE
    WRITE_TO(STDOUT, "%S\n", command);
    #endif
    if (BM->methodology == SHELL_METHODOLOGY)
        rv = (Shell::run(command) == 0)?TRUE:FALSE;
    return rv;
}