Edit

kc3-lang/libxkbcommon/test/external_xkb_compilers.h

Branch :

  • Show log

    Commit

  • Author : Pierre Le Marre
    Date : 2025-03-24 09:21:40
    Hash : 21a341f2
    Message : test: Enable 3rd party compilers

  • test/external_xkb_compilers.h
  • /*
     * Copyright © 2025 Pierre Le Marre <dev@wismill.eu>
     * SPDX-License-Identifier: MIT
     */
    
    #pragma once
    
    #include "config.h"
    
    #include <stdlib.h>
    #include <limits.h>
    #include <stdio.h>
    #include <spawn.h>
    #include <unistd.h>
    #include <assert.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <getopt.h>
    
    #include "utils.h"
    #include "darray.h"
    
    #define PIPE_WRITE 1
    #define PIPE_READ 0
    #define SETUP_FAILURE 99
    
    int
    compile_with(const char* compiler_name, const char* const *compiler_argv,
                 const char *keymap_in, size_t keymap_size_in,
                 char **keymap_out, size_t *keymap_size_out);
    
    int
    compile_with(const char* compiler_name, const char* const *compiler_argv,
                 const char *keymap_in, size_t keymap_size_in,
                 char **keymap_out, size_t *keymap_size_out)
    {
        int ret = EXIT_FAILURE;
        assert(keymap_in);
    
        /* Prepare parameters */
        char *envp[] = { NULL };
    
        /* Prepare input */
        int stdin_pipe[2] = {0};
        int stdout_pipe[2] = {0};
        posix_spawn_file_actions_t action;
        if (pipe(stdin_pipe) == -1) {
            perror("stdin pipe");
            ret = SETUP_FAILURE;
            goto pipe_error;
        }
        if (posix_spawn_file_actions_init(&action)) {
            perror("spawn_file_actions_init error");
            goto posix_spawn_file_actions_init_error;
        }
    
        /* Make spawned process close unused write-end of pipe, else it will not
         * receive EOF when write-end of the pipe is closed below and it will result
         * in a deadlock. */
        if (posix_spawn_file_actions_addclose(&action, stdin_pipe[PIPE_WRITE]))
            goto posix_spawn_file_actions_error;
        /* Make spawned process replace stdin with read end of the pipe */
        if (posix_spawn_file_actions_adddup2(&action, stdin_pipe[PIPE_READ], STDIN_FILENO))
            goto posix_spawn_file_actions_error;
    
        /* Prepare stdout */
        if (pipe(stdout_pipe) == -1) {
            perror("stdout pipe");
            ret = SETUP_FAILURE;
            goto pipe_error;
        }
    
        if (posix_spawn_file_actions_addclose(&action, stdout_pipe[PIPE_READ]))
            goto posix_spawn_file_actions_error;
        if (posix_spawn_file_actions_adddup2(&action, stdout_pipe[PIPE_WRITE], STDOUT_FILENO))
            goto posix_spawn_file_actions_error;
    
        /* Launch compiler */
        pid_t compiler_pid;
        ret = posix_spawnp(&compiler_pid, compiler_name, &action, NULL,
                           (char* const*)compiler_argv, envp);
        if (ret != 0) {
            errno = ret;
            perror("posix_spawnp XKB compiler failed");
            goto posix_spawn_file_actions_init_error;
        }
        /* Close unused read-end of pipe */
        close(stdin_pipe[PIPE_READ]);
        close(stdout_pipe[PIPE_WRITE]);
        ssize_t ret2 = write(stdin_pipe[PIPE_WRITE], keymap_in, keymap_size_in);
        /* Close write-end of the pipe, to emit EOF */
        close(stdin_pipe[PIPE_WRITE]);
        if (ret2 == -1) {
            perror("Cannot write keymap to stdin");
            kill(compiler_pid, SIGTERM);
        }
    
        /* Capture stdout */
        const size_t buffer_size = 1024;
        darray_char stdout = darray_new();
        darray_resize0(stdout, buffer_size);
        size_t size = 0;
        ssize_t count;
        while ((count = read(stdout_pipe[PIPE_READ], darray_items(stdout) + size,
                             buffer_size)) > 0) {
            size += (size_t) count;
            darray_resize0(stdout, size + buffer_size);
        }
        close(stdout_pipe[PIPE_READ]);
    
        /* Ensure we get a NULL-terminated string */
        if (size > 0 && darray_item(stdout, size - 1) != '\0')
            darray_resize0(stdout, size + 1);
    
        darray_steal(stdout, keymap_out, NULL);
        *keymap_size_out = size;
    
        /* Wait for compiler to complete */
        int status;
        ret = waitpid(compiler_pid, &status, 0);
        ret = (ret < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
            ? SETUP_FAILURE
            : EXIT_SUCCESS;
        goto cleanup;
    
    posix_spawn_file_actions_error:
        perror("posix_spawn_file_actions_* error");
    posix_spawn_file_actions_init_error:
        close(stdin_pipe[PIPE_WRITE]);
        close(stdin_pipe[PIPE_READ]);
        close(stdout_pipe[PIPE_READ]);
        close(stdout_pipe[PIPE_WRITE]);
        ret = SETUP_FAILURE;
    cleanup:
        posix_spawn_file_actions_destroy(&action);
    pipe_error:
        return ret;
    }
    
    int
    compile_with_xkbcomp(const char* display, const char *include_path,
                         const char *keymap_in, size_t keymap_size_in,
                         char **keymap_out, size_t *keymap_size_out);
    
    int
    compile_with_xkbcomp(const char* display, const char *include_path,
                         const char *keymap_in, size_t keymap_size_in,
                         char **keymap_out, size_t *keymap_size_out)
    {
        const bool has_display = !isempty(display);
        const char* const out = (has_display) ? display : "-";
    
        /* Prepare xkbcomp parameters */
        char include_path_arg[PATH_MAX+2] = "-I";
        const char *xkbcomp_argv[] = {
            "xkbcomp", "-I" /* reset include path*/, include_path_arg,
            "-opt", "g", "-w", "10", "-xkb", "-" /* stdin */, out, NULL
        };
        if (include_path) {
            int ret = snprintf(include_path_arg, ARRAY_SIZE(include_path_arg),
                               "-I%s", include_path);
            if (ret < 0 || ret >= (int)ARRAY_SIZE(include_path_arg)) {
                return SETUP_FAILURE;
            }
        }
    
        return compile_with("xkbcomp", xkbcomp_argv,
                            keymap_in, keymap_size_in,
                            keymap_out, keymap_size_out);
    }
    
    int
    compile_with_kbvm(const char *include_path,
                      const char *keymap_in, size_t keymap_size_in,
                      char **keymap_out, size_t *keymap_size_out);
    
    int
    compile_with_kbvm(const char *include_path,
                      const char *keymap_in, size_t keymap_size_in,
                      char **keymap_out, size_t *keymap_size_out)
    {
        /* Prepare xkbcomp parameters */
        const char *kbvm_argv[] = {
            "kbvm", "compile-xkb", "-", NULL, NULL, NULL, NULL
        };
        if (include_path) {
            kbvm_argv[3] = "--no-default-includes";
            kbvm_argv[4] = "--append-include";
            kbvm_argv[5] = include_path;
        }
    
        return compile_with("kbvm", kbvm_argv,
                            keymap_in, keymap_size_in,
                            keymap_out, keymap_size_out);
    }