mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-10-05 05:17:59 +03:00
ainedit: initial .pje support
Support building from project files used by the official SDK.
This commit is contained in:
@@ -28,6 +28,7 @@ enum ini_value_type {
|
||||
INI_STRING,
|
||||
INI_LIST,
|
||||
_INI_LIST_ENTRY,
|
||||
INI_FORMATION,
|
||||
|
||||
INI_NR_TYPES
|
||||
};
|
||||
@@ -37,14 +38,21 @@ struct ini_value {
|
||||
union {
|
||||
int i;
|
||||
struct string *s;
|
||||
// list
|
||||
struct {
|
||||
size_t list_size;
|
||||
struct ini_value *list;
|
||||
};
|
||||
// list item
|
||||
struct {
|
||||
size_t _list_pos;
|
||||
struct ini_value *_list_value;
|
||||
};
|
||||
// formation
|
||||
struct {
|
||||
size_t nr_entries;
|
||||
struct ini_entry *entries;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -62,5 +62,6 @@ void string_set_char(struct string **s, int i, unsigned int c);
|
||||
// C strings
|
||||
int int_to_cstr(char *buf, size_t size, int v, int figures, bool zero_pad, bool zenkaku);
|
||||
int float_to_cstr(char *buf, size_t size, float v, int figures, bool zero_pad, int precision, bool zenkaku);
|
||||
struct string *cstr_to_string(const char *str);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -121,6 +121,12 @@ static void ini_free_value(struct ini_value *value)
|
||||
ini_free_value(value->_list_value);
|
||||
free(value->_list_value);
|
||||
break;
|
||||
case INI_FORMATION:
|
||||
for (size_t i = 0; i < value->nr_entries; i++) {
|
||||
ini_free_entry(&value->entries[i]);
|
||||
}
|
||||
free(value->entries);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-9
@@ -38,6 +38,7 @@ unsigned long yini_line = 1;
|
||||
|
||||
%%
|
||||
|
||||
\/\/[^\n]*\n yini_line++;
|
||||
[ \t\r] ;
|
||||
\n yini_line++;
|
||||
;[^\n]*\n yini_line++;
|
||||
@@ -49,6 +50,7 @@ unsigned long yini_line = 1;
|
||||
, return ',';
|
||||
true return TRUE;
|
||||
false return FALSE;
|
||||
Formation return FORMATION;
|
||||
[0-9]+ yini_lval.i = atoi(yini_text); return INTEGER;
|
||||
[a-zA-Z]+[a-zA-Z0-9]* yini_lval.s = make_string(yini_text, yini_leng); return IDENTIFIER;
|
||||
|
||||
@@ -64,15 +66,7 @@ false return FALSE;
|
||||
|
||||
\n ERROR("Unterminated string literal at line %lu", yini_line);
|
||||
|
||||
\\n *string_buf_ptr++ = '\n';
|
||||
\\t *string_buf_ptr++ = '\t';
|
||||
\\r *string_buf_ptr++ = '\r';
|
||||
\\b *string_buf_ptr++ = '\b';
|
||||
\\f *string_buf_ptr++ = '\f';
|
||||
|
||||
\\(.|\n) *string_buf_ptr++ = yini_text[1];
|
||||
|
||||
[^\\\n\"]+ {
|
||||
[^\n\"]+ {
|
||||
char *yptr = yini_text;
|
||||
while (*yptr)
|
||||
*string_buf_ptr++ = *yptr++;
|
||||
|
||||
+21
-1
@@ -43,6 +43,8 @@ static void push_entry(entry_list *ini, struct ini_entry *entry)
|
||||
|
||||
static struct ini_value make_list(value_list *values)
|
||||
{
|
||||
if (!values)
|
||||
return ini_make_list(NULL, 0);
|
||||
struct ini_value *vals = kv_data(*values);
|
||||
size_t nr_vals = kv_size(*values);
|
||||
free(values);
|
||||
@@ -74,9 +76,25 @@ static struct ini_entry *make_list_assign(struct string *name, int i, struct ini
|
||||
return ini_make_entry(name, wrapper);
|
||||
}
|
||||
|
||||
static struct ini_entry *make_formation(struct string *name, entry_list *entries)
|
||||
{
|
||||
struct ini_value wrapper = {
|
||||
.type = INI_FORMATION,
|
||||
.nr_entries = kv_size(*entries),
|
||||
.entries = xcalloc(kv_size(*entries), sizeof(struct ini_entry)),
|
||||
};
|
||||
for (size_t i = 0; i < wrapper.nr_entries; i++) {
|
||||
wrapper.entries[i] = *kv_A(*entries, i);
|
||||
free(kv_A(*entries, i));
|
||||
}
|
||||
kv_destroy(*entries);
|
||||
free(entries);
|
||||
return ini_make_entry(name, wrapper);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%token <token> TRUE FALSE
|
||||
%token <token> TRUE FALSE FORMATION
|
||||
%token <i> INTEGER
|
||||
%token <s> STRING IDENTIFIER
|
||||
|
||||
@@ -99,12 +117,14 @@ entries : entry { $$ = make_ini(); push_entry($$, $1); }
|
||||
|
||||
entry : IDENTIFIER '=' value { $$ = ini_make_entry($1, $3); }
|
||||
| IDENTIFIER '[' INTEGER ']' '=' value { $$ = make_list_assign($1, $3, $6); }
|
||||
| FORMATION STRING '{' entries '}' { $$ = make_formation($2, $4); }
|
||||
;
|
||||
|
||||
value : INTEGER { $$ = ini_make_integer($1); }
|
||||
| TRUE { $$ = ini_make_boolean(true); }
|
||||
| FALSE { $$ = ini_make_boolean(false); }
|
||||
| STRING { $$ = ini_make_string($1); }
|
||||
| '{' '}' { $$ = make_list(NULL); }
|
||||
| '{' list '}' { $$ = make_list($2); }
|
||||
| '{' list ',' '}' { $$ = make_list($2); }
|
||||
;
|
||||
|
||||
+3
-3
@@ -12,6 +12,7 @@ system4 = ['afa.c',
|
||||
'flat.c',
|
||||
'fnl.c',
|
||||
'hashtable.c',
|
||||
'ini.c',
|
||||
'instructions.c',
|
||||
'pms.c',
|
||||
'qnt.c',
|
||||
@@ -30,7 +31,6 @@ xsystem4 = ['audio.c',
|
||||
'fnl_render.c',
|
||||
'format.c',
|
||||
'heap.c',
|
||||
'ini.c',
|
||||
'input.c',
|
||||
'page.c',
|
||||
'resume.c',
|
||||
@@ -75,8 +75,8 @@ bisongen = generator(bison,
|
||||
output: ['@BASENAME@.tab.c', '@BASENAME@.tab.h'],
|
||||
arguments : ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'])
|
||||
|
||||
xsystem4 += flexgen.process('ini_lexer.l')
|
||||
xsystem4 += bisongen.process('ini_parser.y')
|
||||
system4 += flexgen.process('ini_lexer.l')
|
||||
system4 += bisongen.process('ini_parser.y')
|
||||
|
||||
libsys4 = static_library('sys4', system4,
|
||||
dependencies : [libm, zlib, tj, webp, png],
|
||||
|
||||
@@ -54,6 +54,11 @@ struct string *make_string(const char *str, unsigned int len)
|
||||
return s;
|
||||
}
|
||||
|
||||
struct string *cstr_to_string(const char *str)
|
||||
{
|
||||
return make_string(str, strlen(str));
|
||||
}
|
||||
|
||||
struct string *string_ref(struct string *s)
|
||||
{
|
||||
s->cow = 1;
|
||||
|
||||
@@ -32,6 +32,7 @@ static void usage(void)
|
||||
puts(" Edit AIN files.");
|
||||
puts("");
|
||||
puts(" -h, --help Display this message and exit");
|
||||
puts(" -p, --project <pje-file> Build AIN from project file");
|
||||
puts(" -c, --code <jam-file> Update the CODE section (assemble .jam file)");
|
||||
puts(" --jaf <jaf-file> Update AIN file from JAF source code");
|
||||
puts(" -j, --json <json-file> Update AIN file from JSON data");
|
||||
@@ -44,13 +45,13 @@ static void usage(void)
|
||||
puts(" --ain-encoding <enc> Specify the text encoding of the input AIN file");
|
||||
puts(" --ain-version <version> Specify the AIN version (when creating a new AIN file)");
|
||||
puts(" --silent Don't write messages to stdout");
|
||||
//puts(" -p,--project <pje-file> Build AIN from project file");
|
||||
}
|
||||
|
||||
extern int text_parse(void);
|
||||
|
||||
enum {
|
||||
LOPT_HELP = 256,
|
||||
LOPT_PROJECT,
|
||||
LOPT_CODE,
|
||||
LOPT_JAF,
|
||||
LOPT_JSON,
|
||||
@@ -92,6 +93,7 @@ int main(int argc, char *argv[])
|
||||
initialize_instructions();
|
||||
struct ain *ain;
|
||||
int err = AIN_SUCCESS;
|
||||
const char *project_file = NULL;
|
||||
const char *code_file = NULL;
|
||||
unsigned nr_jaf_files = 0;
|
||||
const char **jaf_files = NULL;
|
||||
@@ -107,6 +109,7 @@ int main(int argc, char *argv[])
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{ "help", no_argument, 0, LOPT_HELP },
|
||||
{ "project", required_argument, 0, LOPT_PROJECT },
|
||||
{ "code", required_argument, 0, LOPT_CODE },
|
||||
{ "jaf", required_argument, 0, LOPT_JAF },
|
||||
{ "json", required_argument, 0, LOPT_JSON },
|
||||
@@ -123,7 +126,7 @@ int main(int argc, char *argv[])
|
||||
int option_index = 0;
|
||||
int c;
|
||||
|
||||
c = getopt_long(argc, argv, "hc:j:t:o:", long_options, &option_index);
|
||||
c = getopt_long(argc, argv, "hp:c:j:t:o:", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
@@ -132,6 +135,10 @@ int main(int argc, char *argv[])
|
||||
case LOPT_HELP:
|
||||
usage();
|
||||
return 0;
|
||||
case 'p':
|
||||
case LOPT_PROJECT:
|
||||
project_file = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
case LOPT_CODE:
|
||||
code_file = optarg;
|
||||
@@ -190,11 +197,6 @@ int main(int argc, char *argv[])
|
||||
ERROR("Too many arguments.");
|
||||
}
|
||||
|
||||
if (!output_file) {
|
||||
usage();
|
||||
ERROR("No output file given");
|
||||
}
|
||||
|
||||
if ((ain_conv = iconv_open(output_encoding, input_encoding)) == (iconv_t)-1) {
|
||||
ERROR("iconv_open: %s", strerror(errno));
|
||||
}
|
||||
@@ -205,6 +207,15 @@ int main(int argc, char *argv[])
|
||||
ERROR("iconv_open: %s", strerror(errno));
|
||||
}
|
||||
|
||||
if (project_file) {
|
||||
pje_build(project_file, ain_version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!output_file) {
|
||||
output_file = "out.ain";
|
||||
}
|
||||
|
||||
if (!argc) {
|
||||
ain = ain_new(ain_version);
|
||||
} else {
|
||||
|
||||
@@ -66,6 +66,9 @@ enum asm_pseudo_opcode {
|
||||
|
||||
const struct instruction asm_pseudo_ops[NR_PSEUDO_OPS - PSEUDO_OP_OFFSET];
|
||||
|
||||
// pje.c
|
||||
void pje_build(const char *path, int ain_version);
|
||||
|
||||
// asm.c
|
||||
void asm_assemble_jam(const char *filename, struct ain *ain, uint32_t flags);
|
||||
const struct instruction *asm_get_instruction(const char *name);
|
||||
|
||||
@@ -530,6 +530,7 @@ function_definition
|
||||
function_declarator
|
||||
: IDENTIFIER '(' parameter_list ')' { $$ = jaf_function_declarator($1, $3); }
|
||||
| IDENTIFIER '(' ')' { $$ = jaf_function_declarator($1, NULL); }
|
||||
| IDENTIFIER '(' VOID ')' { $$ = jaf_function_declarator($1, NULL); }
|
||||
;
|
||||
|
||||
parameter_list
|
||||
|
||||
@@ -9,6 +9,7 @@ ainedit = ['../../cJSON.c',
|
||||
'jaf_static_analysis.c',
|
||||
'jaf_types.c',
|
||||
'json.c',
|
||||
'pje.c',
|
||||
'repack.c',
|
||||
'text.c',
|
||||
'transcode.c',
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include "file.h"
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/ini.h"
|
||||
#include "system4/string.h"
|
||||
#include "ainedit.h"
|
||||
#include "jaf.h"
|
||||
|
||||
struct pje_formation {
|
||||
struct string *name;
|
||||
unsigned nr_defines;
|
||||
struct string **defines;
|
||||
};
|
||||
|
||||
struct string_list {
|
||||
unsigned n;
|
||||
struct string **items;
|
||||
};
|
||||
|
||||
struct pje_config {
|
||||
struct string *project_name;
|
||||
struct string *code_name;
|
||||
int game_version;
|
||||
struct string *source_dir;
|
||||
struct string *hll_dir;
|
||||
struct string *obj_dir;
|
||||
struct string *output_dir;
|
||||
unsigned nr_formations;
|
||||
struct pje_formation *formations;
|
||||
struct string_list system_source;
|
||||
struct string_list source;
|
||||
struct string *mod_ain;
|
||||
struct string *mod_text;
|
||||
};
|
||||
|
||||
struct inc_config {
|
||||
struct string_list system_source;
|
||||
struct string_list source;
|
||||
struct string_list copy_to_run;
|
||||
struct string_list copy_to_dll;
|
||||
struct string_list copy_to_dp;
|
||||
struct string_list load_dll;
|
||||
};
|
||||
|
||||
struct build_job {
|
||||
struct string_list system_source;
|
||||
struct string_list source;
|
||||
struct string_list headers;
|
||||
};
|
||||
|
||||
static void string_list_append(struct string_list *list, struct string *str)
|
||||
{
|
||||
list->items = xrealloc_array(list->items, list->n, list->n+1, sizeof(struct string*));
|
||||
list->items[list->n++] = str;
|
||||
}
|
||||
|
||||
static void free_string_list(struct string_list *list)
|
||||
{
|
||||
for (unsigned i = 0; i < list->n; i++) {
|
||||
free_string(list->items[i]);
|
||||
}
|
||||
free(list->items);
|
||||
}
|
||||
|
||||
static struct string *directory_name(const char *path)
|
||||
{
|
||||
char *tmp = strdup(path);
|
||||
char *dir = dirname(tmp);
|
||||
struct string *r = make_string(dir, strlen(dir));
|
||||
free(tmp);
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct string *directory_file(struct string *dir, struct string *file)
|
||||
{
|
||||
char tmp[1024];
|
||||
if (dir->size + file->size + 2 >= 1024)
|
||||
ERROR("path too long");
|
||||
|
||||
memcpy(tmp, dir->text, dir->size);
|
||||
tmp[dir->size] = '/';
|
||||
memcpy(tmp+dir->size+1, file->text, file->size);
|
||||
tmp[dir->size+file->size+1] = '\0';
|
||||
|
||||
for (int i = dir->size+1; i < dir->size + file->size + 1; i++) {
|
||||
if (tmp[i] == '\\')
|
||||
tmp[i] = '/';
|
||||
}
|
||||
|
||||
return make_string(tmp, dir->size+file->size+1);
|
||||
}
|
||||
|
||||
static struct string *pje_string(struct ini_entry *entry)
|
||||
{
|
||||
if (entry->value.type != INI_STRING)
|
||||
ERROR("Invalid value for '%s': not a string", entry->name->text);
|
||||
return string_dup(entry->value.s);
|
||||
}
|
||||
|
||||
static int pje_integer(struct ini_entry *entry)
|
||||
{
|
||||
if (entry->value.type != INI_INTEGER)
|
||||
ERROR("Invalid value for '%s': not an integer", entry->name->text);
|
||||
return entry->value.i;
|
||||
}
|
||||
|
||||
static void pje_string_list(struct ini_entry *entry, struct string_list *out)
|
||||
{
|
||||
if (entry->value.type != INI_LIST)
|
||||
ERROR("Invalid value for '%s': not a list", entry->name->text);
|
||||
|
||||
out->n = entry->value.list_size;
|
||||
out->items = xcalloc(entry->value.list_size, sizeof(struct string*));
|
||||
|
||||
for (unsigned i = 0; i < out->n; i++) {
|
||||
if (entry->value.list[i].type != INI_STRING)
|
||||
ERROR("Invalid value in '%s': not a string", entry->name->text);
|
||||
out->items[i] = string_dup(entry->value.list[i].s);
|
||||
}
|
||||
}
|
||||
|
||||
static void pje_parse(const char *path, struct pje_config *config)
|
||||
{
|
||||
size_t ini_size;
|
||||
struct ini_entry *ini = ini_parse(path, &ini_size);
|
||||
|
||||
for (size_t i = 0; i < ini_size; i++) {
|
||||
if (!strcmp(ini[i].name->text, "ProjectName")) {
|
||||
config->project_name = pje_string(&ini[i]);
|
||||
} else if (!strcmp(ini[i].name->text, "CodeName")) {
|
||||
config->code_name = pje_string(&ini[i]);
|
||||
} else if (!strcmp(ini[i].name->text, "GameVersion")) {
|
||||
config->game_version = pje_integer(&ini[i]);
|
||||
} else if (!strcmp(ini[i].name->text, "SourceDir")) {
|
||||
config->source_dir = pje_string(&ini[i]);
|
||||
} else if (!strcmp(ini[i].name->text, "HLLDir")) {
|
||||
config->hll_dir = pje_string(&ini[i]);
|
||||
} else if (!strcmp(ini[i].name->text, "ObjDir")) {
|
||||
config->obj_dir = pje_string(&ini[i]);
|
||||
} else if (!strcmp(ini[i].name->text, "OutputDir")) {
|
||||
config->output_dir = pje_string(&ini[i]);
|
||||
} else if (!strcmp(ini[i].name->text, "SystemSource")) {
|
||||
pje_string_list(&ini[i], &config->system_source);
|
||||
} else if (!strcmp(ini[i].name->text, "Source")) {
|
||||
pje_string_list(&ini[i], &config->source);
|
||||
} else if (!strcmp(ini[i].name->text, "HLL")) {
|
||||
WARNING("HLL renaming not supported");
|
||||
} else if (!strcmp(ini[i].name->text, "SyncFolder")) {
|
||||
WARNING("SyncFolder not supported");
|
||||
} else if (!strcmp(ini[i].name->text, "SyncLock")) {
|
||||
WARNING("SyncLock not supported");
|
||||
} else if (!strcmp(ini[i].name->text, "ModAin")) {
|
||||
config->mod_ain = pje_string(&ini[i]);
|
||||
} else if (!strcmp(ini[i].name->text, "ModText")) {
|
||||
config->mod_text = pje_string(&ini[i]);
|
||||
} else if (ini[i].value.type == INI_FORMATION) {
|
||||
WARNING("Formations not supported");
|
||||
} else {
|
||||
ERROR("Unhandled pje variable: %s", ini[i].name->text);
|
||||
}
|
||||
ini_free_entry(&ini[i]);
|
||||
}
|
||||
free(ini);
|
||||
|
||||
// defaults
|
||||
if (!config->project_name)
|
||||
config->project_name = cstr_to_string("UntitledProject");
|
||||
if (!config->code_name)
|
||||
config->code_name = cstr_to_string("out.ain");
|
||||
if (!config->source_dir)
|
||||
config->source_dir = cstr_to_string("source");
|
||||
if (!config->hll_dir)
|
||||
config->hll_dir = cstr_to_string("hll");
|
||||
if (!config->obj_dir)
|
||||
config->obj_dir = cstr_to_string("obj");
|
||||
if (!config->output_dir)
|
||||
config->output_dir = cstr_to_string("run");
|
||||
}
|
||||
|
||||
static void pje_parse_inc(const char *path, struct inc_config *config)
|
||||
{
|
||||
size_t ini_size;
|
||||
struct ini_entry *ini = ini_parse(path, &ini_size);
|
||||
|
||||
for (size_t i = 0; i < ini_size; i++) {
|
||||
if (!strcmp(ini[i].name->text, "Source")) {
|
||||
pje_string_list(&ini[i], &config->source);
|
||||
} else if (!strcmp(ini[i].name->text, "SystemSource")) {
|
||||
pje_string_list(&ini[i], &config->system_source);
|
||||
} else if (!strcmp(ini[i].name->text, "CopyToRun")) {
|
||||
pje_string_list(&ini[i], &config->copy_to_run);
|
||||
} else if (!strcmp(ini[i].name->text, "CopyToDLL")) {
|
||||
pje_string_list(&ini[i], &config->copy_to_dll);
|
||||
} else if (!strcmp(ini[i].name->text, "CopyToDP")) {
|
||||
pje_string_list(&ini[i], &config->copy_to_dp);
|
||||
} else if (!strcmp(ini[i].name->text, "LoadDLL")) {
|
||||
pje_string_list(&ini[i], &config->load_dll);
|
||||
} else {
|
||||
ERROR("Unhandled variable in file '%s': %s", path, ini[i].name->text);
|
||||
}
|
||||
ini_free_entry(&ini[i]);
|
||||
}
|
||||
free(ini);
|
||||
}
|
||||
|
||||
static void pje_free_inc(struct inc_config *config)
|
||||
{
|
||||
free_string_list(&config->system_source);
|
||||
free_string_list(&config->source);
|
||||
free_string_list(&config->copy_to_run);
|
||||
free_string_list(&config->copy_to_dll);
|
||||
free_string_list(&config->copy_to_dp);
|
||||
free_string_list(&config->load_dll);
|
||||
}
|
||||
|
||||
static const char *extname(const char *s)
|
||||
{
|
||||
const char *e = strrchr(s, '.');
|
||||
return e ? e+1 : NULL;
|
||||
}
|
||||
|
||||
static void pje_read_source(struct build_job *job, struct string *dir, struct string_list *source, bool system);
|
||||
|
||||
static void pje_read_inc(struct build_job *job, struct string *dir, struct string *inc)
|
||||
{
|
||||
struct inc_config config = {0};
|
||||
struct string *file = directory_file(dir, inc);
|
||||
struct string *file_dir = directory_name(file->text);
|
||||
pje_parse_inc(file->text, &config);
|
||||
|
||||
pje_read_source(job, file_dir, &config.system_source, true);
|
||||
pje_read_source(job, file_dir, &config.source, false);
|
||||
|
||||
pje_free_inc(&config);
|
||||
free_string(file);
|
||||
free_string(file_dir);
|
||||
}
|
||||
|
||||
static void pje_read_source(struct build_job *job, struct string *dir, struct string_list *source, bool system)
|
||||
{
|
||||
for (unsigned i = 0; i < source->n; i++) {
|
||||
const char *ext = extname(source->items[i]->text);
|
||||
if (!strcmp(ext, "inc")) {
|
||||
pje_read_inc(job, dir, source->items[i]);
|
||||
} else if (!strcmp(ext, "jaf")) {
|
||||
string_list_append(system ? &job->system_source : &job->source, directory_file(dir, source->items[i]));
|
||||
} else if (!strcmp(ext, "hll")) {
|
||||
if (i+1 >= source->n)
|
||||
ERROR("Missing HLL name in source list: %s", source->items[i]->text);
|
||||
if (strchr(source->items[i+1]->text, '.'))
|
||||
ERROR("HLL name contains '.': %s", source->items[i+1]->text);
|
||||
string_list_append(&job->headers, directory_file(dir, source->items[i]));
|
||||
string_list_append(&job->headers, directory_file(dir, source->items[i+1]));
|
||||
i++;
|
||||
} else {
|
||||
ERROR("Unhandled file extension in source list: '%s'", ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pje_free(struct pje_config *config)
|
||||
{
|
||||
if (config->project_name)
|
||||
free_string(config->project_name);
|
||||
if (config->code_name)
|
||||
free_string(config->code_name);
|
||||
if (config->source_dir)
|
||||
free_string(config->source_dir);
|
||||
if (config->hll_dir)
|
||||
free_string(config->hll_dir);
|
||||
if (config->obj_dir)
|
||||
free_string(config->obj_dir);
|
||||
if (config->output_dir)
|
||||
free_string(config->output_dir);
|
||||
free_string_list(&config->system_source);
|
||||
free_string_list(&config->source);
|
||||
}
|
||||
|
||||
static void build_job_free(struct build_job *job)
|
||||
{
|
||||
free_string_list(&job->system_source);
|
||||
free_string_list(&job->source);
|
||||
free_string_list(&job->headers);
|
||||
}
|
||||
|
||||
void pje_build(const char *path, int ain_version)
|
||||
{
|
||||
struct build_job job = {0};
|
||||
struct pje_config config = {0};
|
||||
pje_parse(path, &config);
|
||||
|
||||
struct string *pje_dir = directory_name(path);
|
||||
struct string *src_dir = directory_file(pje_dir, config.source_dir);
|
||||
struct string *out_dir = directory_file(pje_dir, config.output_dir);
|
||||
if (mkdir_p(out_dir->text)) {
|
||||
ERROR("Creating output directory '%s': %s", out_dir->text, strerror(errno));
|
||||
}
|
||||
|
||||
// collect source file names
|
||||
pje_read_source(&job, src_dir, &config.system_source, true);
|
||||
pje_read_source(&job, src_dir, &config.source, false);
|
||||
|
||||
// TODO: parse hll files
|
||||
for (unsigned i = 0; i < job.headers.n; i++) {
|
||||
WARNING("Ignoring header: %s", job.headers.items[i]->text);
|
||||
}
|
||||
|
||||
// consolidate files into list
|
||||
// TODO: It's unclear from the docs if ALL SystemSource files should be compiled
|
||||
// before ALL Source files. This becomes an issue if .inc files are used
|
||||
// which contain both SystemSource and Source lists. E.g., should
|
||||
// SystemSource files included from the top-level Source list be compiled
|
||||
// before Source files included from the top-level SystemSource list?
|
||||
// This needs to be confirmed before implementing global constructors.
|
||||
unsigned nr_source_files = job.system_source.n + job.source.n;
|
||||
const char **source_files = xcalloc(nr_source_files, sizeof(const char*));
|
||||
for (unsigned i = 0; i < job.system_source.n; i++) {
|
||||
source_files[i] = job.system_source.items[i]->text;
|
||||
}
|
||||
for (unsigned i = 0; i < job.source.n; i++) {
|
||||
source_files[job.system_source.n + i] = job.source.items[i]->text;
|
||||
}
|
||||
|
||||
// open/create ain object
|
||||
struct ain *ain;
|
||||
if (config.mod_ain) {
|
||||
int err;
|
||||
struct string *mod_ain = directory_file(pje_dir, config.mod_ain);
|
||||
if (!(ain = ain_open(mod_ain->text, &err))) {
|
||||
ERROR("Failed to open ain file: %s", ain_strerror);
|
||||
}
|
||||
free_string(mod_ain);
|
||||
} else {
|
||||
ain = ain_new(ain_version);
|
||||
}
|
||||
|
||||
// apply text substitution, if ModText given
|
||||
if (config.mod_text) {
|
||||
struct string *mod_text = directory_file(pje_dir, config.mod_text);
|
||||
read_text(mod_text->text, ain);
|
||||
free_string(mod_text);
|
||||
}
|
||||
|
||||
// build
|
||||
jaf_build(ain, source_files, nr_source_files);
|
||||
|
||||
// write to disk
|
||||
NOTICE("Writing AIN file...");
|
||||
struct string *output_file = directory_file(out_dir, config.code_name);
|
||||
ain_write(output_file->text, ain);
|
||||
|
||||
free_string(pje_dir);
|
||||
free_string(src_dir);
|
||||
free_string(out_dir);
|
||||
free_string(output_file);
|
||||
free(source_files);
|
||||
build_job_free(&job);
|
||||
pje_free(&config);
|
||||
ain_free(ain);
|
||||
}
|
||||
@@ -3,6 +3,6 @@ int main()
|
||||
int a;
|
||||
ref int ra = a;
|
||||
ref int rb = ra;
|
||||
ra = 1;
|
||||
rb = 1;
|
||||
return !(a == 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user