mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement INCLUDE_ONCE directive (#1481)
Identify files by (device, inode), not by path, so that symlinks, relative paths, case-insensitive paths, or other edge cases do not result in double includes.
This commit is contained in:
@@ -17,6 +17,12 @@
|
||||
|
||||
#include "asm/lexer.hpp"
|
||||
|
||||
enum IncludeType {
|
||||
INCLUDE_NORMAL,
|
||||
INCLUDE_PRE,
|
||||
INCLUDE_ONCE
|
||||
};
|
||||
|
||||
struct FileStackNode {
|
||||
FileStackNodeType type;
|
||||
Either<
|
||||
@@ -64,7 +70,7 @@ void fstk_SetPreIncludeFile(std::string const &path);
|
||||
std::optional<std::string> fstk_FindFile(std::string const &path);
|
||||
|
||||
bool yywrap();
|
||||
void fstk_RunInclude(std::string const &path, bool updateStateNow);
|
||||
void fstk_RunInclude(std::string const &path, IncludeType type);
|
||||
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs);
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span);
|
||||
void fstk_RunFor(
|
||||
|
||||
12
man/rgbasm.5
12
man/rgbasm.5
@@ -2158,7 +2158,17 @@ calls infinitely (or until you run out of memory, whichever comes first).
|
||||
INCLUDE "irq.inc"
|
||||
.Ed
|
||||
.Pp
|
||||
You may also implicitly
|
||||
You may also ensure a file only gets included once by using
|
||||
.Ic INCLUDE_ONCE
|
||||
instead of
|
||||
.Ic INCLUDE .
|
||||
This will skip including a file if it has already been included before (with
|
||||
.Ic INCLUDE ,
|
||||
.Ic INCLUDE_ONCE ,
|
||||
or
|
||||
.Fl P ) .
|
||||
.Pp
|
||||
You can implicitly
|
||||
.Ic INCLUDE
|
||||
a file before the source file with the
|
||||
.Fl P
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <utility>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "helpers.hpp"
|
||||
@@ -45,8 +47,8 @@ size_t maxRecursionDepth;
|
||||
|
||||
// The first include path for `fstk_FindFile` to try is none at all
|
||||
static std::vector<std::string> includePaths = {""};
|
||||
|
||||
static std::string preIncludeName;
|
||||
static std::set<std::pair<dev_t, ino_t>> includedFiles;
|
||||
|
||||
std::string const &FileStackNode::dump(uint32_t curLineNo) const {
|
||||
if (data.holds<std::vector<uint32_t>>()) {
|
||||
@@ -291,11 +293,11 @@ static Context &newReptContext(int32_t reptLineNo, ContentSpan const &span, uint
|
||||
return context;
|
||||
}
|
||||
|
||||
void fstk_RunInclude(std::string const &path, bool preInclude) {
|
||||
void fstk_RunInclude(std::string const &path, IncludeType type) {
|
||||
std::optional<std::string> fullPath = fstk_FindFile(path);
|
||||
|
||||
if (!fullPath) {
|
||||
if (generatedMissingIncludes && !preInclude) {
|
||||
if (generatedMissingIncludes && type != INCLUDE_PRE) {
|
||||
if (verbose)
|
||||
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n", path.c_str(), strerror(errno));
|
||||
failedOnMissingInclude = true;
|
||||
@@ -305,6 +307,24 @@ void fstk_RunInclude(std::string const &path, bool preInclude) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The pair of device ID and serial number uniquely identify a file, with `stat()`
|
||||
// following symbolic links to identify the actual file.
|
||||
struct stat statBuf;
|
||||
if (stat(fullPath->c_str(), &statBuf) != 0) {
|
||||
error("Failed to stat file '%s': %s\n", fullPath->c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
std::pair<dev_t, ino_t> inode{statBuf.st_dev, statBuf.st_ino};
|
||||
|
||||
if (type == INCLUDE_ONCE && includedFiles.find(inode) != includedFiles.end()) {
|
||||
if (verbose) {
|
||||
printf("File '%s' already included, skipping INCLUDE_ONCE", path.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
includedFiles.insert(inode);
|
||||
|
||||
if (!newFileContext(*fullPath, false))
|
||||
fatalerror("Failed to set up lexer for file include\n");
|
||||
}
|
||||
@@ -395,5 +415,5 @@ void fstk_Init(std::string const &mainPath, size_t maxDepth) {
|
||||
maxRecursionDepth = maxDepth;
|
||||
|
||||
if (!preIncludeName.empty())
|
||||
fstk_RunInclude(preIncludeName, true);
|
||||
fstk_RunInclude(preIncludeName, INCLUDE_PRE);
|
||||
}
|
||||
|
||||
@@ -256,6 +256,7 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
|
||||
{"INCHARMAP", T_(OP_INCHARMAP) },
|
||||
|
||||
{"INCLUDE", T_(POP_INCLUDE) },
|
||||
{"INCLUDE_ONCE", T_(POP_INCLUDE_ONCE) },
|
||||
{"PRINT", T_(POP_PRINT) },
|
||||
{"PRINTLN", T_(POP_PRINTLN) },
|
||||
{"EXPORT", T_(POP_EXPORT) },
|
||||
|
||||
@@ -236,6 +236,7 @@
|
||||
%token POP_IF "IF"
|
||||
%token POP_INCBIN "INCBIN"
|
||||
%token POP_INCLUDE "INCLUDE"
|
||||
%token POP_INCLUDE_ONCE "INCLUDE_ONCE"
|
||||
%token POP_LOAD "LOAD"
|
||||
%token POP_MACRO "MACRO"
|
||||
%token POP_NEWCHARMAP "NEWCHARMAP"
|
||||
@@ -464,6 +465,7 @@ line_directive:
|
||||
| for
|
||||
| break
|
||||
| include
|
||||
| include_once
|
||||
| if
|
||||
// It's important that all of these require being at line start for `skipIfBlock`
|
||||
| elif
|
||||
@@ -1140,7 +1142,15 @@ export_def:
|
||||
|
||||
include:
|
||||
label POP_INCLUDE string endofline {
|
||||
fstk_RunInclude($3, false);
|
||||
fstk_RunInclude($3, INCLUDE_NORMAL);
|
||||
if (failedOnMissingInclude)
|
||||
YYACCEPT;
|
||||
}
|
||||
;
|
||||
|
||||
include_once:
|
||||
label POP_INCLUDE_ONCE string endofline {
|
||||
fstk_RunInclude($3, INCLUDE_ONCE);
|
||||
if (failedOnMissingInclude)
|
||||
YYACCEPT;
|
||||
}
|
||||
|
||||
3
test/asm/include-once.asm
Normal file
3
test/asm/include-once.asm
Normal file
@@ -0,0 +1,3 @@
|
||||
INCLUDE_ONCE "include-once.inc"
|
||||
INCLUDE_ONCE "include-once.inc"
|
||||
INCLUDE_ONCE "include-link.inc"
|
||||
1
test/asm/include-once.inc
Normal file
1
test/asm/include-once.inc
Normal file
@@ -0,0 +1 @@
|
||||
DEF HELLO EQU 1
|
||||
@@ -11,9 +11,12 @@ input="$(mktemp)"
|
||||
output="$(mktemp)"
|
||||
errput="$(mktemp)"
|
||||
|
||||
# Create a symbolic link for the `include-once.asm` test case.
|
||||
ln include-once.inc include-link.inc
|
||||
|
||||
# Immediate expansion is the desired behavior.
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -f ${o@Q} ${gb@Q} ${input@Q} ${output@Q} ${errput@Q}" EXIT
|
||||
trap "rm -f ${o@Q} ${gb@Q} ${input@Q} ${output@Q} ${errput@Q} include-link.inc" EXIT
|
||||
|
||||
tests=0
|
||||
failed=0
|
||||
|
||||
Reference in New Issue
Block a user