#!/usr/bin/env bash
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
- echo "This script must be sourced, not executed. Exiting."
- return 1
-fi
-
-# Note these suffixes:
+# The build environment.
+#
+# Hopefully no edits are required here to build this project's programs.
+#
+# The most common edit here is to add the name of a new program to the EXECUTOR_IN_FL.
+# That variable is found in the next section.
+#
+# The `env_dev` script should have been sourced already. (env_dev is analogous
+# to the Python virutal environment `activate`.) `env_dev` will set `developer/executor/`
+# first in the search PATH. In that directory there is a script called `make` which
+# is a wrapper to `/bin/make`. That script sources this file. Hence, this file
+# is the environment for make, aka the build environment.
+#
+# Note these suffixes found on variable names:
# _FL = File List
# _FP = File Path
# _FPL = File Path List
# _DL = Directory List
# _DP = Directory path
# _DPL = Directory path list
-
+#
# _PRIMARY = stuff not built
# _IN things input to some program
# _OUT things output by some program
+#
+# Relative paths values are relative to the $REPO_HOME/developer directory, unless
+# for good reason which is obvious or stated in a comment adjacent to the variable.
-#--------------------------------------------------------------------------------
-# commands
-
-export BIN_MAKE=/bin/make
+# Ensure the script is sourced rather than run directly
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+ echo "This script must be sourced, not executed. Exiting."
+ return 1
+fi
#--------------------------------------------------------------------------------
-# to be built
+# programs to be built
+# each new executable will also need a custom target in makefile-project.mk
-# export PROGRAM_PrintRuleNameList="executor/PrintRuleNameList"
-# export PROGRAM_SyntaxTree_Arithmetic="executor/SyntaxTree_Arithmetic"
-# export PROGRAM_Test__SyntaxTree_Arithmetic="executor/Test__SyntaxTree_Arithmetic"
-# export PROGRAM_SyntaxTree_20240412="executor/SyntaxTree_20240412"
-
-
-# Add each new executable to this list, and give it a custom target in the makefile.
export EXECUTOR_IN_FL="\
PrintRuleNameList\
TerminalToCategory\
#
# set by project manager: JAVA_HOME, ANTLR_JAR, DEVELOPER_HOME
#
+export BIN_MAKE=/bin/make
export JAVA_COMP="${JAVA_HOME}/bin/javac"
export JAVA_INTERP="${JAVA_HOME}/bin/java"
export JAVA_ARCHIVE="${JAVA_HOME}/bin/jar"
#--------------------------------------------------------------------------------
# ANTLR files
#
-
export ANTLR_IN_PRIMARY_FPL=$(ls ${ANTLR_IN_PRIMARY_DIR}/*.g4 2>/dev/null | tr '\n' ' ')
export ANTLR_GRAMMAR_LIST=$(basename -s .g4 ${ANTLR_IN_PRIMARY_FPL})
if [ -z "${ANTLR_IN_PRIMARY_FPL}" ]; then
echo "No ANTLR input grammar files found"
fi
-# replaced with a tool called ANTLR_OUR_FL
-#
-# This function accepts a grammar name, or a grammar name with an extension;
-# and sets a variable of the form ANTLR_OUT_<grammar>_FPL to a list of
-# files that ANTLR would produce for <grammar>.
-# set_ANTLR_out_fpl_var() {
-# local grammar=$1
-# export ANTLR_OUT_${grammar}_FPL="${ANTLR_OUT_DIR}/${grammar}Lexer.java ${ANTLR_OUT_DIR}/${grammar}Parser.java ${ANTLR_OUT_DIR}/${grammar}BaseVisitor.java ${ANTLR_OUT_DIR}/${grammar}Visitor.java"
-# }
-
-# Generate ANTLR_OUT_<grammar>_FPL for each grammar
-# for grammar in ${ANTLR_GRAMMAR_LIST}; do
-# set_ANTLR_out_fpl_var ${grammar}
-# done
-
-# Combine all individual file lists into ANTLR_OUT_FPL
-# ANTLR_OUT_FPL=""
-# for grammar in ${ANTLR_GRAMMAR_LIST}; do
-# ANTLR_OUT_FPL="${ANTLR_OUT_FPL} $(eval echo \$ANTLR_OUT_${grammar}_FPL)"
-# done
-# export ANTLR_OUT_FPL
-
#--------------------------------------------------------------------------------
# Java files
#
# in case there have been edits to the environment
source "${EXECUTOR_IN_DIR}"/env_build
+# If make is called with no arguments, we will make the entire project.
+# Making the project has a dependency on the tools, so if they have not
+# been made, they will be made before the project is made.
+if [[ $# -eq 0 ]]; then
+ set -- "project"
+fi
+
${BIN_MAKE} --no-print-directory -f "${EXECUTOR_IN_DIR}"/makefile-top.mk $@
-
-$(info makefile: $(MAKEFILE_LIST))
-$(info project_MAKECMDGOALS: $(MAKECMDGOALS))
+#--------------------------------------------------------------------------------
+# Project build
+#
# turn off implicit rules
.SUFFIXES:
MAKEFLAGS += -r
# `make` always tries to make its makefiles as targets. This prevents that.
-.SECONDARY: $(MAKEFILE_LIST)
+.PHONY: $(MAKEFILE_LIST)
+.PRECIOUS: $(MAKEFILE_LIST)
+
+$(info makefile: $(MAKEFILE_LIST))
+$(info project_MAKECMDGOALS: $(MAKECMDGOALS))
#================================================================================
# Custom make targets
#
project: $(EXECUTOR_IN_FPL)
-
PrintRuleNameList: $(EXECUTOR_IN_DIR)/PrintRuleNameList
#-----------------------------------------------
# Arithmetic
-ANTLR_OUT_Arithmetic_FL := $(shell ANTLR_OUT_FL Arithmetic.g4 -visitor -no-listener -no-tokens)
-ANTLR_OUT_Arithmetic_FPL := $(addprefix $(ANTLR_OUT_DIR)/, $(ANTLR_OUT_Arithmetic_FL))
-Arithmetic_Echo: $(ANTLR_OUT_Arithmetic_FPL) $(JAVA_COMP_IN_PRIMARY_DIR)/Arithmetic_Echo_PrintVisitor.java
+ANTLR_OUT_Arithmetic_FPL $(shell ANTLR_OUT_FL Arithmetic -path $(ANTLR_OUT_DIR))
+Arithmetic_Echo:\
+ $(ANTLR_OUT_Arithmetic_FPL)
+ $(JAVA_COMP_IN_PRIMARY_DIR)/Arithmetic_Echo_PrintVisitor.java
@if [ -z "$(ANTLR_OUT_Arithmetic_FPL)" ]; then \
echo "variable ANTLR_OUT_Arithmetic_FPL empty."; \
exit 1; \
fi
make $(EXECUTOR_IN_DIR)/Arithmetic_Echo
-# Arithmetic_Echo__Test: $(ANTLR_OUT_Arithmetic_FPL) $(JAVA_COMP_IN_PRIMARY_DIR)/Arithmetic_Echo_PrintVisitor.java
-# @if [ -z "$(ANTLR_OUT_Arithmetic_FPL)" ]; then \
-# echo "variable ANTLR_OUT_Arithmetic_FPL empty."; \
-# exit 1; \
-# fi
-# $(BIN_MAKE) $(EXECUTOR_IN_DIR)/Arithmetic_Echo__Test
-
-# Arithmetic_Syntax: $(ANTLR_OUT_Arithmetic_FPL) $(JAVA_COMP_IN_PRIMARY_DIR)/Arithmetic_Syntax_PrintVisitor.java
-# @if [ -z "$(ANTLR_OUT_Arithmetic_FPL)" ]; then \
-# echo "variable ANTLR_OUT_Arithmetic_FPL empty."; \
-# exit 1; \
-# fi
-# $(BIN_MAKE) $(EXECUTOR_IN_DIR)/Arithmetic_Syntax
-
-# Arithmetic_Syntax__Test: $(ANTLR_OUT_Arithmetic_FPL) $(JAVA_COMP_IN_PRIMARY_DIR)/Arithmetic_Syntax_PrintVisitor.java
-# @if [ -z "$(ANTLR_OUT_Arithmetic_FPL)" ]; then \
-# echo "variable ANTLR_OUT_Arithmetic_FPL empty."; \
-# exit 1; \
-# fi
-# $(BIN_MAKE) $(EXECUTOR_IN_DIR)/Arithmetic_Syntax__Test
+Arithmetic_Echo__Test:\
+ $(ANTLR_OUT_Arithmetic_FPL)
+ $(JAVA_COMP_IN_PRIMARY_DIR)/Arithmetic_Echo_PrintVisitor.java
+ @if [ -z "$(ANTLR_OUT_Arithmetic_FPL)" ]; then \
+ echo "variable ANTLR_OUT_Arithmetic_FPL empty."; \
+ exit 1; \
+ fi
+ $(BIN_MAKE) $(EXECUTOR_IN_DIR)/Arithmetic_Echo__Test
+
+Arithmetic_Syntax:\
+ $(ANTLR_OUT_Arithmetic_FPL)
+ $(JAVA_COMP_IN_PRIMARY_DIR)/Arithmetic_Syntax_PrintVisitor.java
+ @if [ -z "$(ANTLR_OUT_Arithmetic_FPL)" ]; then \
+ echo "variable ANTLR_OUT_Arithmetic_FPL empty."; \
+ exit 1; \
+ fi
+ $(BIN_MAKE) $(EXECUTOR_IN_DIR)/Arithmetic_Syntax
+
+Arithmetic_Syntax__Test:\
+ $(ANTLR_OUT_Arithmetic_FPL)
+ $(JAVA_COMP_IN_PRIMARY_DIR)/Arithmetic_Syntax_PrintVisitor.java
+ @if [ -z "$(ANTLR_OUT_Arithmetic_FPL)" ]; then \
+ echo "variable ANTLR_OUT_Arithmetic_FPL empty."; \
+ exit 1; \
+ fi
+ $(BIN_MAKE) $(EXECUTOR_IN_DIR)/Arithmetic_Syntax__Test
#-----------------------------------------------
# GQL_20240412
-ANTLR_OUT_GQL_20240412_FL := $(shell ANTLR_OUT_FL GQL_20240412.g4 -visitor -no-listener -no-tokens)
-ANTLR_OUT_GQL_20240412_FPL := $(addprefix $(ANTLR_OUT_DIR)/, $(ANTLR_OUT_GQL_20240412_FL))
-
+ANTLR_OUT_GQL_20240412_FPL := $(shell ANTLR_OUT_FL GQL_20240412 -path $(ANTLR_OUT_DIR))
GQL_20240412_Syntax: $(ANTLR_OUT_GQL_20240412_FPL) $(JAVA_COMP_IN_PRIMARY_DIR)/GQL_20240412_Syntax_PrintVisitor.java
@if [ -z "$(ANTLR_OUT_GQL_20240412_FPL)" ]; then \
echo "variable ANTLR_OUT_GQL_20240412_FPL empty."; \
-$(info makefile: $(MAKEFILE_LIST))
-$(info project_MAKECMDGOALS: $(MAKECMDGOALS))
-
# turn off implicit rules
.SUFFIXES:
MAKEFLAGS += -r
# `make` always tries to make its makefiles as targets. This prevents that.
-.SECONDARY: $(MAKEFILE_LIST)
+.PHONY: $(MAKEFILE_LIST)
+.PRECIOUS: $(MAKEFILE_LIST)
+
+$(info makefile: $(MAKEFILE_LIST))
+$(info project_MAKECMDGOALS: $(MAKECMDGOALS))
#================================================================================
# Custom make targets
-$(info makefile: $(MAKEFILE_LIST))
-$(info project_MAKECMDGOALS: $(MAKECMDGOALS))
+# In GNU make, pattern matches can not be empty strings.
+# `> make project`, or `make project-project` makes the entire project
+# `> make tool-tool` makes all the tools (while not making the project).
# turn off implicit rules
.SUFFIXES:
MAKEFLAGS += -r
-# `make` always tries to make its makefiles as targets. This prevents that.
-.SECONDARY: $(MAKEFILE_LIST)
+# `make` always tries to make its make files as target files. This prevents that.
+.PHONY: $(MAKEFILE_LIST)
+.PRECIOUS: $(MAKEFILE_LIST)
+
+$(info makefile: $(MAKEFILE_LIST))
+$(info project_MAKECMDGOALS: $(MAKECMDGOALS))
+
#================================================================================
# Custom make targets
#
-.PHONY: top version clean setup tool- project-
+.PHONY: top version clean setup
# The 'all' target now depends on 'tool' and 'project'
-top: setup tool- project-
+top: setup tool-tool project-project
# for distinguishing between make syntax errors and build errors
nothing:
# `clean` is program independent of `make`
clean:
- @echo "Use the clean script from the $(EXECUTOR_IN_DIR) directory instead of \`make clean\`"
- @$(EXECUTOR_IN_DIR)/clean
+ @echo "Use the command `clean <option>` instead of make.`"
setup:
mkdir -p $(ANTLR_IN_PRIMARY_DIR) $(JAVA_COMP_IN_PRIMARY_DIR) $(JVM_IN_DIR)
- mkdir -p $(EXECUTOR_IN_DIR) test deprecated experiment documentation temporary
+ mkdir -p $(EXECUTOR_IN_DIR) test deprecated experiment ologist temporary
# Ensure tools are built before building the project programs
tool-%: setup
$(BIN_MAKE) -f $(EXECUTOR_IN_DIR)/makefile-tool.mk -$(MAKEFLAGS) $*
-project-%: tool
+project-%: tool-tool
$(BIN_MAKE) -f $(EXECUTOR_IN_DIR)/makefile-project.mk -$(MAKEFLAGS) $*
# delegate other targets to the project
/*
+Run the command with no arguments for a usage message.
Accepts an antlr grammar file name of the form: [path/]<name>[Lexer/Parser][.g4]
-Prints a space separated list of files antlr4 would output.
+Prints a space-separated list of files antlr4 would output.
The <name>Lexer or <name>Parser suffix, or absence thereof, tell this program if
antlr would create lexer and parser files, or both.
-The `-visitor` and `no-lsistner` options also affect the output, as they would
-for antlr.
*/
+
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ANTLR_OUT_FL {
+ // Constant for the usage message
+ private static final String USAGE_MESSAGE = "Usage: java ANTLR_OUT_FL <grammar-file> " +
+ "[-visitor (default)] [-no-visitor] " +
+ "[-listener] [-no-listener (default)] " +
+ "[-tokens] [-no-tokens (default)] " +
+ "[-path <path>]";
+
public static void main(String[] args) {
if (args.length == 0) {
- System.err.println("Usage: java ANTLR_OUT_FL <grammar-file> [-visitor] [-no-listener]");
+ System.err.println(USAGE_MESSAGE);
System.exit(1);
}
- boolean visitor = false;
- boolean noListener = false;
- boolean noTokens = false;
+ // Defaults
+ boolean visitor = true;
+ boolean noListener = true;
+ boolean noTokens = true;
+ String outputPath = ""; // Default empty path
List<String> argList = new ArrayList<>();
// Parse the arguments
- for (String arg : args) {
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
if (arg.startsWith("-")) {
switch (arg) {
case "-visitor":
visitor = true;
break;
+ case "-no-visitor":
+ visitor = false;
+ break;
+ case "-listener":
+ noListener = false;
+ break;
case "-no-listener":
noListener = true;
break;
+ case "-tokens":
+ noTokens = false;
+ break;
case "-no-tokens":
noTokens = true;
break;
+ case "-path":
+ // Ensure the next argument exists and isn't another option
+ if (i + 1 < args.length && !args[i + 1].startsWith("-")) {
+ outputPath = args[++i]; // Get the next argument as the path
+ if (!outputPath.endsWith("/")) {
+ outputPath += "/"; // Ensure the path ends with a slash
+ }
+ } else {
+ System.err.println(USAGE_MESSAGE);
+ System.exit(1);
+ }
+ break;
default:
System.err.println("Unrecognized option: " + arg);
- System.err.println("Usage: java ANTLR_OUT_FL <grammar-file> [-visitor] [-no-listener]");
+ System.err.println(USAGE_MESSAGE);
System.exit(1);
}
} else {
// Ensure there is exactly one grammar file argument
if (argList.size() != 1) {
- System.err.println("Usage: java ANTLR_OUT_FL <grammar-file> [-visitor] [-no-listener]");
+ System.err.println(USAGE_MESSAGE);
System.exit(1);
}
String grammarFile = argList.get(0);
- List<String> generatedFiles = generateFileList(grammarFile, visitor, noListener ,noTokens);
+ List<String> generatedFiles = generateFileList(grammarFile, visitor, noListener, noTokens, outputPath);
// Print the files in a space-separated format on a single line
if (!generatedFiles.isEmpty()) {
System.out.println(); // Print a newline at the end
}
- public static List<String> generateFileList(String grammarFile, boolean visitor, boolean noListener ,boolean noTokens) {
+ public static List<String> generateFileList(String grammarFile, boolean visitor, boolean noListener, boolean noTokens, String outputPath) {
String baseName = new File(grammarFile).getName().replace(".g4", "");
List<String> fileList = new ArrayList<>();
if (isLexer || isCombined) {
// Lexer files
- fileList.add(baseName + "Lexer.java");
- if (!noTokens) fileList.add(baseName + "Lexer.tokens");
+ fileList.add(outputPath + baseName + "Lexer.java");
+ if (!noTokens) fileList.add(outputPath + baseName + "Lexer.tokens");
}
if (isParser || isCombined) {
// Parser files
- fileList.add(baseName + "Parser.java");
- if (!noTokens) fileList.add(baseName + ".tokens");
+ fileList.add(outputPath + baseName + "Parser.java");
+ if (!noTokens) fileList.add(outputPath + baseName + ".tokens");
// Listener-related files
if (!noListener) {
- fileList.add(baseName + "Listener.java");
- fileList.add(baseName + "BaseListener.java");
+ fileList.add(outputPath + baseName + "Listener.java");
+ fileList.add(outputPath + baseName + "BaseListener.java");
}
// Visitor-related files
if (visitor) {
- fileList.add(baseName + "Visitor.java");
- fileList.add(baseName + "BaseVisitor.java");
+ fileList.add(outputPath + baseName + "Visitor.java");
+ fileList.add(outputPath + baseName + "BaseVisitor.java");
}
}
export PATH="$DEVELOPER_HOME"/executor:"$REPO_HOME"/tool/executor:"$JAVA_HOME"/bin:"$PATH"
cd "$DEVELOPER_HOME"
-source "$DEVELOPER_HOME"/executor/env_build
echo "${BASH_SOURCE[0]}" "complete"
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Building the Project: A Developer's Guide</title>
+</head>
+<body>
+
+ <h1>Building the Project: A Developer’s Guide</h1>
+
+ <p>This document is intended to guide developers on how to build existing programs and extend the build environment when needed.</p>
+
+ <h2>Section 1: Building Existing Programs</h2>
+
+ <h3>1.1 Prerequisites</h3>
+ <ul>
+ <li><strong>Setting Up Your Environment:</strong>
+ Ensure that <code>make</code> is available in your system’s <code>PATH</code>.
+ Make sure necessary environment variables such as <code>DEVELOPER_HOME</code> and <code>EXECUTOR_IN_DIR</code> are set.
+ Source the <code>env_build</code> script to configure the environment:
+ <pre><code>source /path/to/env_build</code></pre>
+ </li>
+ </ul>
+
+ <h3>1.2 Basic Build Process</h3>
+ <ul>
+ <li><strong>How to Use Make:</strong>
+ Developers can use the <code>make</code> command to build specific programs.
+ For example:
+ <pre><code>make X</code></pre>
+ Here, <code>X</code> is the name of the program you wish to build.
+ If you do not specify any program, <code>make</code> will build the entire project:
+ <pre><code>make</code></pre>
+ </li>
+ <li><strong>Sample Commands:</strong>
+ <pre><code>make Arithmetic_Echo</code></pre>
+ <pre><code>make Arithmetic_Syntax</code></pre>
+ </li>
+ </ul>
+
+ <h3>1.3 Understanding the Output</h3>
+ <ul>
+ <li>After a successful build, executables will be created in the <code>executor</code> directory.
+ <br>The <code>makefile-top.mk</code> file will be the top-level control file for managing the build process.
+ </li>
+ </ul>
+
+ <h2>Section 2: Advanced Usage for Developers</h2>
+
+ <h3>2.1 Adding New Programs</h3>
+ <ul>
+ <li><strong>Steps to Add a New Program:</strong>
+ <ul>
+ <li>Modify <code>makefile-project.mk</code> and <code>makefile-tool.mk</code> to include the new program.</li>
+ <li>Add the program name to the <code>EXECUTOR_IN_FL</code> list in <code>env_build</code>.</li>
+ <li>Ensure that source files (e.g., <code>.java</code> or ANTLR grammar files) are properly placed in the relevant directories.</li>
+ </ul>
+ </li>
+ </ul>
+
+ <h3>2.2 Advanced Make Commands</h3>
+ <ul>
+ <li><strong>Using <code>project-X</code> and <code>tool-Y</code>:</strong>
+ Developers can use advanced make commands to build only specific components.
+ For example:
+ <pre><code>make project-Arithmetic_Syntax</code></pre>
+ This command builds the <code>Arithmetic_Syntax</code> component only.
+ Similarly, use <code>make tool-X</code> to build a specific tool:
+ <pre><code>make tool-Y</code></pre>
+ </li>
+ </ul>
+
+ <h3>2.3 Debugging and Extending the Build Environment</h3>
+ <ul>
+ <li><strong>Environment Variables:</strong>
+ Developers can modify environment variables in the <code>env_build</code> script to add or remove programs, directories, or paths.
+ <br>Common variables include:
+ <ul>
+ <li><code>EXECUTOR_IN_FL</code>: The list of programs to build.</li>
+ <li><code>ANTLR_OUT_DIR</code>: Directory for ANTLR-generated files.</li>
+ <li><code>JAVA_COMP_IN_FPL</code>: File list for Java compilation.</li>
+ </ul>
+ </li>
+ </ul>
+
+</body>
+</html>
+
+