Search code examples
csyntasticpremake

How can I generate a Syntastic config file using premake?


Syntastic is a source code linter plugin for the Vim editor.

It does various syntax and heuristic checks, using external tools. In the case of C and C++ code, this frequently involves running a compiler on the code.

In order to invoke the compiler, Syntastic reads a config file that contains command-line arguments that should be used to invoke the compiler.

Obviously, the "real" compilation in the project is handled by premake but this means there are potentially two sources of truth -- the compiler flags written into the Syntastic config file, and the compiler flags written by premake into the build scripts.

I'd like to resolve this by having premake generate the compiler flags in the Syntastic config file, also.

This seems like a fairly straightforward task with various possible approaches -- generate a fake compiler, invent a pre-build task, etc. But I don't know enough about the innards of premake to know which of these approaches is the right one.

How can I get premake to generate my syntastic.config file?


Solution

  • I looked into a few different kinds of projects under premake5 -- there have been several added to the documentation recently. None of them quite got me there.

    The problem is that what I want to do is either "always create the file" or "create the file when the premake5.lua file changes." That makes things difficult, but not totally impossible.

    However, the generated file is not an input file for any of the projects, so that apparently pushes it over the edge from difficult to "not possible ATM".

    I tried doing what premake calls Custom Build Commands, but this needs the generated file to be a piece of source code it can use to build with. If this isn't the case, apparently the dependency graph breaks and the makefile is generated without a dependency on the file. :(

    In order to just get this working, I opted to just regenerate the file all the time using a prebuild command:

    prebuildcommands {
        "$(SILENT) "
        ..repo_dir.."etc/write-syntastic-config.sh $(ALL_CFLAGS)"
        .." > "
        ..repo_dir.."etc/syntastic.config"
    }
    

    The key value in this is $(ALL_CFLAGS), the variable used by the Makefile generator to hold the stuff I want.

    Syntastic's config file and its use of the config file is a bit touchy. It doesn't accept -L and -l options, or perhaps gcc doesn't accept them in whatever mode it is invoked with. Regardless, some filtering is required the args can be used.

    Also, syntastic processes the lines looking specifically for -I, which it then handles in a special way: the include paths are treated as either absolute, or relative to the config file.

    Finally, syntastic doesn't appear to know about -isystem, another include-directory option for gcc. So that has to be handles differently.

    If anyone cares, here's the script I'm using:

    etc/write-syntastic-config.sh

    #!/bin/bash
    set -euo pipefail
    IFS=$'\n\t'
    
    PENDING=""
    PENDING_DIR=false
    
    printf '# Generated by %s\n' "$(basename "$0")"
    
    CFG_FILE_DIR="$(dirname "$0")"
    
    for ARG in "$@"
    do
        #printf >&2 "arg=>%s<\\n" "$ARG"
    
        case "$ARG" in
        -I| \
        -L)
            PENDING="$ARG" 
            PENDING_DIR=true
            ;;
    
        -isystem)
            # -isystem is like -I, except syntastic doesn't know it
            printf '%s\n' "$ARG"
            PENDING_DIR=true
            ;;
    
        -I*|    \
        -L*)    PENDING_DIR=true
            PENDING="${ARG:0:2}"
            ARG="${ARG/#-?/}"
    
            ;;& #<-- Resume matching cases (i.e, "goto next case")
    
        *)  if $PENDING_DIR
            then
                ARG="$(realpath -m              \
                    --relative-to "$CFG_FILE_DIR"       \
                    "$ARG")"
            fi
    
            case "$PENDING" in
            -L) : ignore it ;;
            "") printf '%s\n' "$ARG" ;;
            *)  printf '%s %s\n' "$PENDING" "$ARG" ;;
            esac
    
            PENDING=""
            PENDING_DIR=false
            ;;
        esac
    done
    
    exit 0