Browse Source

Added --execute option

This allows Cakelisp code to be built and run all in one command. This
gives a similar feel to writing quick scripts, without all the
overhead scripts usually bring.
HotReloadingWithCodeModification
Macoy Madson 7 months ago
parent
commit
8dd893704f
7 changed files with 91 additions and 3 deletions
  1. +1
    -0
      .gitignore
  2. +63
    -1
      src/Main.cpp
  3. +3
    -1
      src/ModuleManager.cpp
  4. +1
    -1
      src/ModuleManager.hpp
  5. +14
    -0
      src/RunProcess.cpp
  6. +2
    -0
      src/RunProcess.hpp
  7. +7
    -0
      test/Execute.cake

+ 1
- 0
.gitignore View File

@ -34,6 +34,7 @@ src/dynamicLoadTest
src/runProcessTest
src/dependencyTest
runtime/HotReloadingTest
test/ExecuteMe
*.cake.cpp
*.cake.hpp


+ 63
- 1
src/Main.cpp View File

@ -3,8 +3,10 @@
#include <vector>
#include "FileUtilities.hpp"
#include "Logging.hpp"
#include "ModuleManager.hpp"
#include "RunProcess.hpp"
#include "Utilities.hpp"
struct CommandLineOption
@ -31,10 +33,15 @@ void printHelp(const CommandLineOption* options, int numOptions)
}
}
void OnExecuteProcessOutput(const char* output)
{
}
int main(int numArguments, char* arguments[])
{
bool enableHotReloading = false;
bool ignoreCachedFiles = false;
bool executeOutput = false;
const CommandLineOption options[] = {
{"--ignore-cache", &ignoreCachedFiles,
@ -43,6 +50,10 @@ int main(int numArguments, char* arguments[])
"build without having to delete the Cakelisp cache directory"},
{"--enable-hot-reloading", &enableHotReloading,
"Generate code so that objects defined in Cakelisp can be reloaded at runtime"},
{"--execute", &executeOutput,
"If building completes successfully, run the output executable. Its working directory "
"will be the final location of the executable. This allows Cakelisp code to be run as if "
"it were a script"},
// Logging
{"--verbose-tokenization", &log.tokenization,
"Output details about the conversion from file text to tokens"},
@ -172,12 +183,63 @@ int main(int numArguments, char* arguments[])
printf("\nBuild:\n");
if (!moduleManagerBuild(moduleManager))
std::vector<std::string> builtOutputs;
if (!moduleManagerBuild(moduleManager, builtOutputs))
{
moduleManagerDestroy(moduleManager);
return 1;
}
if (executeOutput)
{
printf("\nExecute:\n");
if (builtOutputs.empty())
{
printf("error: --execute: No executables were output\n");
moduleManagerDestroy(moduleManager);
return 1;
}
// TODO: Allow user to forward arguments to executable
for (const std::string& output : builtOutputs)
{
RunProcessArguments arguments = {};
// Need to use absolute path when executing
const char* executablePath = makeAbsolutePath_Allocated(nullptr, output.c_str());
arguments.fileToExecute = executablePath;
const char* commandLineArguments[] = {strdup(arguments.fileToExecute), nullptr};
arguments.arguments = commandLineArguments;
char workingDirectory[MAX_PATH_LENGTH] = {0};
getDirectoryFromPath(arguments.fileToExecute, workingDirectory,
ArraySize(workingDirectory));
arguments.workingDir = workingDirectory;
int status = 0;
if (runProcess(arguments, &status) != 0)
{
printf("error: execution of %s failed\n", output.c_str());
free((void*)executablePath);
free((void*)commandLineArguments[0]);
moduleManagerDestroy(moduleManager);
return 1;
}
waitForAllProcessesClosed(OnExecuteProcessOutput);
free((void*)executablePath);
free((void*)commandLineArguments[0]);
if (status != 0)
{
printf("error: execution of %s returned non-zero exit code %d\n", output.c_str(),
status);
moduleManagerDestroy(moduleManager);
return status;
}
}
}
moduleManagerDestroy(moduleManager);
return 0;
}

+ 3
- 1
src/ModuleManager.cpp View File

@ -405,7 +405,7 @@ void builtObjectsFree(std::vector<BuiltObject*>& objects)
objects.clear();
}
bool moduleManagerBuild(ModuleManager& manager)
bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtOutputs)
{
int currentNumProcessesSpawned = 0;
@ -637,6 +637,7 @@ bool moduleManagerBuild(ModuleManager& manager)
printf("Skipping linking (no built objects are newer than cached executable)\n");
printf("Successfully built and linked %s (no changes)\n", outputExecutableName.c_str());
builtOutputs.push_back(outputExecutableName);
builtObjectsFree(builtObjects);
return true;
@ -700,6 +701,7 @@ bool moduleManagerBuild(ModuleManager& manager)
}
printf("Successfully built and linked %s\n", outputExecutableName.c_str());
builtOutputs.push_back(outputExecutableName);
builtObjectsFree(builtObjects);
return true;


+ 1
- 1
src/ModuleManager.hpp View File

@ -59,4 +59,4 @@ bool moduleLoadTokenizeValidate(const char* filename, const std::vector<Token>**
bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename);
bool moduleManagerEvaluateResolveReferences(ModuleManager& manager);
bool moduleManagerWriteGeneratedOutput(ModuleManager& manager);
bool moduleManagerBuild(ModuleManager& manager);
bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtOutputs);

+ 14
- 0
src/RunProcess.cpp View File

@ -1,6 +1,7 @@
#include "RunProcess.hpp"
#include <stdio.h>
#include <vector>
#ifdef UNIX
@ -113,8 +114,21 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
nonConstArguments[numArgs] = nullptr;
}
if (arguments.workingDir)
{
if (chdir(arguments.workingDir) != 0)
{
perror("RunProcess chdir: ");
goto childProcessFailed;
}
if (log.processes)
printf("Set working directory to %s\n", arguments.workingDir);
}
systemExecute(arguments.fileToExecute, nonConstArguments);
childProcessFailed:
// This shouldn't happen unless the execution failed or soemthing
for (char** arg = nonConstArguments; *arg != nullptr; ++arg)
delete *arg;


+ 2
- 0
src/RunProcess.hpp View File

@ -8,6 +8,8 @@
struct RunProcessArguments
{
const char* fileToExecute;
// nullptr = no change (use parent process's working dir)
const char* workingDir;
const char** arguments;
};


+ 7
- 0
test/Execute.cake View File

@ -0,0 +1,7 @@
(c-import "<stdio.h>")
(defun main (&return int)
(printf "Hello, execute!\n")
(return 0))
(set-cakelisp-option executable-output "test/ExecuteMe")

Loading…
Cancel
Save