254 lines
12 KiB
C
254 lines
12 KiB
C
// The MIT License (MIT)
|
|
//
|
|
// Copyright (C) 2026 Scott Duensing
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
|
|
// basBuild.h -- shared "compile a project into an .app" back end
|
|
//
|
|
// Both the DVX BASIC IDE (ideMain.c, Make Executable) and the standalone
|
|
// command-line compiler (bascomp.c) turn a compiled module plus a set of
|
|
// .frm texts into a finished .app. The whole back half of that job --
|
|
// release stripping, form/control name obfuscation, bytecode compaction,
|
|
// serialization, stub extraction, resource emission, help-file copy and
|
|
// the "noicon" fallback -- lives here so both front ends produce
|
|
// byte-identical artifacts.
|
|
//
|
|
// Callers own every buffer they pass in via BasBuildSpecT; the module is
|
|
// never modified (release builds work on a private copy).
|
|
|
|
#ifndef BAS_BUILD_H
|
|
#define BAS_BUILD_H
|
|
|
|
#include "compiler/compiler.h"
|
|
#include "runtime/vm.h"
|
|
#include "dvxPrefs.h"
|
|
#include "dvxTypes.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
// Progress callback (may be NULL); receives one line per pipeline stage.
|
|
typedef void (*BasBuildLogFnT)(const char *msg);
|
|
|
|
typedef struct {
|
|
// Project metadata (any NULL/empty is skipped; projName falls back
|
|
// to a generic label).
|
|
const char *projName;
|
|
const char *author;
|
|
const char *publisher;
|
|
const char *version;
|
|
const char *copyright;
|
|
const char *description;
|
|
|
|
// Project directory; iconPath and helpFile are relative to it.
|
|
const char *projectDir;
|
|
const char *iconPath; // NULL/empty: embed the NOICON fallback
|
|
const char *helpFile; // NULL/empty: no help file
|
|
|
|
// A file carrying the STUB and NOICON resources (bascomp: itself);
|
|
// NULL or "" means BAS_BUILD_STUB_CARRIER.
|
|
const char *selfPath;
|
|
|
|
// Compiled module. Never modified.
|
|
const BasModuleT *module;
|
|
|
|
// Raw .frm texts (NUL-terminated), one per form. Comments are
|
|
// stripped here; release builds additionally obfuscate them.
|
|
const char *const *frmSources;
|
|
int32_t frmCount;
|
|
|
|
// .ctl header texts (the "Begin UserControl ... End" block of each, as
|
|
// basBuildAddSource keeps them), one per UserControl: CTLn resources.
|
|
const char *const *ctlSources;
|
|
int32_t ctlCount;
|
|
|
|
bool release;
|
|
bool multiInstance; // [Settings] MultiInstance: more than one copy may run
|
|
BasBuildLogFnT log; // may be NULL
|
|
} BasBuildSpecT;
|
|
|
|
// Where a build inside DVX finds the STUB and NOICON resources: the
|
|
// basdev library carries them (it is installed wherever programs can be
|
|
// compiled), relative to the DVX folder like every installed path.
|
|
#define BAS_BUILD_STUB_CARRIER "libs" DVX_PATH_SEP "kpunch" DVX_PATH_SEP "basdev" DVX_PATH_SEP "basdev.lib"
|
|
|
|
// Build outPath from spec. Returns NULL on success, otherwise a static
|
|
// error message. On failure outPath may be left partially written.
|
|
const char *basBuildApp(const char *outPath, const BasBuildSpecT *spec);
|
|
|
|
// Longest error text basBuildConcatProject reports (a path plus a reason).
|
|
#define BAS_BUILD_ERROR_LEN (DVX_MAX_PATH + 64)
|
|
|
|
// One source file's span in the concatenated project source: lines
|
|
// [startLine, startLine + lineCount) of the concatenation are lines
|
|
// [firstFileLine, firstFileLine + lineCount) of path.
|
|
typedef struct {
|
|
char path[DVX_MAX_PATH];
|
|
int32_t startLine;
|
|
int32_t lineCount;
|
|
int32_t firstFileLine; // 1 for a module; the first code line of a .frm
|
|
} BasSourceMapT;
|
|
|
|
// A project's BASIC source as the compiler consumes it: every [Modules]
|
|
// file in order wrapped in BEGINMODULE "name" / ENDMODULE, then the code
|
|
// of every [Classes] file wrapped in BEGINCLASS "name" / ENDCLASS, then
|
|
// the code of every [Controls] file wrapped in BEGINCLASS "name"
|
|
// USERCONTROL / ENDCLASS, then the code section of every [Forms] file
|
|
// wrapped in BEGINFORM "name" / ENDFORM, each file ending on a newline.
|
|
// frmPaths and frmTexts list the .frm files (path and full text, as read)
|
|
// in the same order for the resource step, ctlTexts the .ctl headers;
|
|
// map locates each file's lines.
|
|
typedef struct {
|
|
char *source; // NUL-terminated, malloc'd
|
|
int32_t sourceLen;
|
|
int32_t sourceCap;
|
|
int32_t lineCount; // newlines in source (next text starts on lineCount + 1)
|
|
char **frmPaths; // stb_ds array of malloc'd paths
|
|
char **frmTexts; // stb_ds array of malloc'd .frm texts
|
|
char **ctlTexts; // stb_ds array of malloc'd .ctl header texts (Begin UserControl ... End)
|
|
BasSourceMapT *map; // stb_ds array, one entry per file
|
|
int32_t fileCount; // modules + classes + controls + forms
|
|
bool optionExplicit; // [Settings] OptionExplicit
|
|
char error[BAS_BUILD_ERROR_LEN];
|
|
} BasProjectSourceT;
|
|
|
|
// What basBuildAppendFile wraps a file's text in.
|
|
typedef enum {
|
|
BAS_BUILD_WRAP_NONE, // the text as it is (a lone program)
|
|
BAS_BUILD_WRAP_MODULE, // a project's module: BEGINMODULE "name" / ENDMODULE (its Private CONST / ENUM stay in it)
|
|
BAS_BUILD_WRAP_FORM, // a form's code: BEGINFORM "name" / ENDFORM
|
|
BAS_BUILD_WRAP_CLASS, // a class module's code: BEGINCLASS "name" / ENDCLASS
|
|
BAS_BUILD_WRAP_CONTROL // a UserControl's code: BEGINCLASS "name" USERCONTROL / ENDCLASS
|
|
} BasBuildWrapE;
|
|
|
|
// Appends one file's text to *ps (a zeroed struct is a valid empty
|
|
// project) and makes sure it ends on a newline, wrapped as wrap says in
|
|
// the directives naming name; a form's BEGINFORM line also lists its
|
|
// controls, ctrlList (basFrmControlList's text; NULL or "" for none),
|
|
// and a UserControl's BEGINCLASS line its USERCONTROL mark (ctrlList is
|
|
// not used).
|
|
// textLine and textLines (either may be
|
|
// NULL) receive the 1-based line the text starts on and the number of
|
|
// lines it occupies, wrapper lines excluded -- what an IDE source map
|
|
// needs. Returns false with ps->error set on failure.
|
|
bool basBuildAppendFile(BasProjectSourceT *ps, const char *text, int32_t len, BasBuildWrapE wrap, const char *name, const char *ctrlList, int32_t *textLine, int32_t *textLines);
|
|
|
|
// Reads the source files named by an open .dbp (paths relative to
|
|
// projectDir) into *out. Returns false with out->error set on failure;
|
|
// call basBuildFreeProject either way.
|
|
bool basBuildConcatProject(PrefsHandleT *prefs, const char *projectDir, BasProjectSourceT *out);
|
|
|
|
// Writes the directory part of path into out, or "." when path has none.
|
|
void basBuildDirOf(const char *path, char *out, int32_t outSize);
|
|
|
|
// Releases everything basBuildConcatProject allocated.
|
|
void basBuildFreeProject(BasProjectSourceT *out);
|
|
|
|
// Why basBuildCompile failed.
|
|
typedef enum {
|
|
BAS_COMPILE_OK, // compiled
|
|
BAS_COMPILE_ERROR, // an error in the source (line / col set)
|
|
BAS_COMPILE_NO_MEMORY, // out of memory
|
|
BAS_COMPILE_NO_MODULE // compiled, but the module could not be built
|
|
} BasCompileResultE;
|
|
|
|
typedef struct {
|
|
BasCompileResultE result;
|
|
int32_t line; // 1-based line of the source (0: none)
|
|
int32_t col; // 1-based column (0: not known)
|
|
char message[BAS_BUILD_ERROR_LEN]; // basCompilerError's text, or the failure
|
|
} BasCompileErrorT;
|
|
|
|
// Compiles source (len bytes) into a module (the caller frees it with
|
|
// basModuleFree), with OPTION EXPLICIT for the whole program when
|
|
// optionExplicit is set and validator (may be NULL) checking
|
|
// CtrlName.Member references. *liveCompiler (may be NULL) holds the
|
|
// compiler while it runs: a validator reads its form scope from it.
|
|
// NULL on failure, with *err telling why.
|
|
BasModuleT *basBuildCompile(const char *source, int32_t len, bool optionExplicit, const BasCtrlValidatorT *validator, const BasCompilerT **liveCompiler, BasCompileErrorT *err);
|
|
|
|
// A compiler message without its "Line N, Col M: " prefix.
|
|
const char *basBuildErrorText(const char *message);
|
|
|
|
// Appends one source file (path names it in the source map; text is its
|
|
// whole text, len bytes, NUL-terminated) the way a project is built: a
|
|
// module wrapped in BEGINMODULE, a class module's code after its header
|
|
// in BEGINCLASS, a UserControl's code after its header in BEGINCLASS ...
|
|
// USERCONTROL (the header is kept in ctlTexts), a form's code section in
|
|
// BEGINFORM with its controls (the .frm text is kept in frmPaths /
|
|
// frmTexts for the resources and the run), a lone program
|
|
// (BAS_BUILD_WRAP_NONE) as it is. Returns false
|
|
// with ps->error set on failure.
|
|
bool basBuildAddSource(BasProjectSourceT *ps, const char *path, const char *text, int32_t len, BasBuildWrapE wrap);
|
|
|
|
// Maps a 1-based line of the concatenated source to the file and 1-based
|
|
// line it came from, using a source map (stb_ds array, e.g. ps->map).
|
|
// Returns false (outputs untouched) for a line outside every file, e.g.
|
|
// a BEGINFORM/ENDFORM wrapper line.
|
|
bool basBuildMapLine(const BasSourceMapT *map, int32_t line, const char **path, int32_t *fileLine);
|
|
|
|
// The inverse of basBuildMapLine: the 1-based line of the concatenated
|
|
// source holding line fileLine of path (compared as DOS compares names:
|
|
// letters in any case, '/' and '\\' alike),
|
|
// or 0 when path is not in the map or the line was not compiled (a class
|
|
// module's header, a .frm's layout).
|
|
int32_t basBuildMapFileLine(const BasSourceMapT *map, const char *path, int32_t fileLine);
|
|
|
|
// Opens a .dbp project: *prefs receives the loaded INI and projectDir
|
|
// (projectDirSize bytes) the directory holding it. Returns false when
|
|
// the file cannot be opened (prefsLoad alone would open a missing file
|
|
// as an empty project).
|
|
bool basBuildOpenProject(const char *dbpPath, PrefsHandleT **prefs, char *projectDir, int32_t projectDirSize);
|
|
|
|
// Fills spec's project metadata (name, author, publisher, version,
|
|
// copyright, description, icon, help file, MultiInstance) and projectDir
|
|
// from an open .dbp; the strings stay owned by prefs. The other fields
|
|
// are left alone.
|
|
void basBuildSpecFromProject(PrefsHandleT *prefs, const char *projectDir, BasBuildSpecT *spec);
|
|
|
|
// Build outPath, a control file (.wgt holding only resources: the marker
|
|
// DVX_RES_ONLY_MAGIC, then the module, each UserControl's header as CTLn
|
|
// with its toolbox icon24 and name resources -- "-2", "-3" ... for the
|
|
// second and later -- the project's metadata and help file) from spec, a
|
|
// control project's (no forms; ctlSources needed). NULL on success,
|
|
// else a static error message.
|
|
const char *basBuildControl(const char *outPath, const BasBuildSpecT *spec);
|
|
|
|
// True when the open .dbp is a control project ([Project] Type = Control).
|
|
bool basBuildIsControlProject(PrefsHandleT *prefs);
|
|
|
|
// True when type, a [Project] Type value, makes a control project (any
|
|
// case).
|
|
bool basBuildIsControlType(const char *type);
|
|
|
|
// The kind of project file path names, by its extension: a form (.frm),
|
|
// a class module (.cls), a UserControl (.ctl) or a module (anything
|
|
// else).
|
|
BasBuildWrapE basBuildWrapForPath(const char *path);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // BAS_BUILD_H
|