Compile Command
1. What is it ?
1.1. Why ?
Before answering what this tool is, let’s begin by why this tool exists ?
Distributing scripts that rely on multiple sourced files is inconvenient, as users must unpack and run them from specific locations. To improve this, the goal is to create a single script file that embeds all necessary files. This approach ensures:
- Easy distribution as a single file
- Reliable copy-paste across systems and editors
- Support for embedding binary (non-printable) files
- Reusability of bash functions
To achieve this, the script must store other files’ contents in a way that avoids non-printable characters, which can cause issues during copy-paste or transmission over messaging programs.
1.2. what is a bash framework function ?
The so called bash framework functions are the functions defined in this framework that respects the following naming
convention:
- Namespace::Namespace::functionName
- we can have any number of namespaces
- each namespace is followed by ::
- namespace must begin by an uppercase letter
[A-Z]followed by any of these characters[A-Za-z0-9_-]. - the function name is traditionally written using camelCase with first letter in small case
- function name authorized characters are
[a-zA-Z0-9_-]+
- the function source code using namespace convention will be searched under srcDirs provided to the compiler via
–src-dir argument
- each namespace corresponds to a folder
- the filename of the function is the function name with .sh extension
- eg: Filters::camel2snakeCase source code can be found in src/Filters/camel2snakeCase.sh
1.3. what does this compiler tool do ?
This tool allows to detect all the framework functions used inside a given sh file. The framework functions matches the
pattern Namespace::functionName (we can have several namespaces separated by the characters ::). These framework
functions will be injected inside a compiled file. The process is recursive so that every framework functions used by
imported framework functions will be imported as well (of course only once).
You can see several examples of compiled files by checking bash-tools-framework src/_binaries folder
2. The compile command
Description: This command inlines all the functions used in the script given in parameter
Usage:
bin/compile` [-h|--help] prints this help and exits
Usage:
bin/compile <fileToCompile>
[--src-dir|-s <srcDir>]
[--bin-dir|-b <binDir>] [--bin-file|-f <binFile>]
[--root-dir|-r <rootDir>] [--src-path <srcPath>]
[--template <templateName>] [--keep-temp-files|-k]
Mandatory Arguments:
<fileToCompile>the relative or absolute path to compile into one file
Options:
--help,-hprints this help and exits--src-dir|-s <srcDir>provide the directory where to find the functions source code. By default this project src directory is used.You can add as much –src-dir options as needed to define other source dirs.
The functions will be searched in the order defined (it allows function redefinition)
Example:
--src-dir src --src-dir otherSrcFunctions::myFunctionwill be searched in- src/Functions/myFunction.sh
- otherSrc/Functions/myFunction.sh
Important Note: if you provide a
--src-dirand you need also functions defined in this project, think about adding a--src-dirfor this project too.--bin-dir|-b <binDir>allows to override the value ofFRAMEWORK_BIN_DIR. By default FRAMEWORK_BIN_DIR is set tobindirectory below the folder abovebin/compile.--bin-file|-f <binFile>BIN_FILEdirective will be overridden bybinFilevalue. See more information below about directives.--template-dir|-t <templateDir>the template directory used to override some template includes. See more information below about environment variables.--root-dir|-r <rootDir>if you wish to overrideFRAMEWORK_ROOT_DIRvariable.By default root directory is the folder above
bin/compile.--src-path <path>if you wish to override the filepath that will be displayed in the header to indicate the src filepath that has been compiled (SRC_FILE_PATH).By default, it is initialized with path relative to
FRAMEWORK_ROOT_DIR--keep-temp-files|-kkeep temporary files for debug purpose
Examples:
Let’s say you want to generate the binary file bin/shellcheckLint from the source file
examples/configReference/shellcheckLint.yaml
bin/compile "$(pwd)/examples/configReference/shellcheckLint.yaml" --src-dir "$(pwd)/src" \
--bin-dir "$(pwd)/bin" --root-dir "$(pwd)"
Here you want to generate the binary but overriding some or all functions of vendor/bash-tools-framework/src using
src folder
bin/compile "$(pwd)/examples/configReference/shellcheckLint.yaml" --s "$(pwd)/src" \
-s "$(pwd)/vendor/bash-tools-framework/src" --bin-dir "$(pwd)/bin" --root-dir "$(pwd)"
Here you want to override the default templates too
bin/compile "$(pwd)/examples/configReference/shellcheckLint.yaml" --s "$(pwd)/src" \
-s "$(pwd)/vendor/bash-tools-framework/src" --bin-dir "$(pwd)/bin" \
--root-dir "$(pwd)" --template-dir "$(pwd)/src/templates"
3. The compiler - How this compiler works ?
3.1. Template variables
Other variables are automatically generated to be used in your templates:
ORIGINAL_TEMPLATE_DIRallowing you to include the template relative to the script being interpretedTEMPLATE_DIRthe template directory in which you can override the templates defined inORIGINAL_TEMPLATE_DIR
The following variables depends upon parameters passed to this script:
SRC_FILE_PATHthe src file you want to show at the top of generated file to indicate from which source file the binary has been generated.SRC_ABSOLUTE_PATHis the path of the file being compiled, it can be useful if you need to access a path relative to this file during compilation.
3.2. Compiler Algorithm
The command to generate a bash binary file:
./bin/bash-compiler examples/configReference/shellcheckLint.yaml \
--root-dir /home/wsl/fchastanet/bash-dev-env/vendor/bash-tools-framework \
--target-dir examples/generated \
--keep-intermediate-files
This will trigger the following actions

Source code: Activity diagram
@startuml "compiler"
title compiler algorithm
skinparam {
' https://github.com/plantuml/plantuml/blob/49115dfc7d4156961e5b49a81c09b474daa79823/src/net/sourceforge/plantuml/style/FromSkinparamToStyle.java#L145
activityDiamondBackgroundColor #AAAAAA
activityEndColor #red
}
start
:compile binaryModelYamlFile;
#SpringGreen:model.LoadBinaryModel binaryModelYamlFile;
if (ok) then (binaryModel)
else (error)
stop
endif
partition "compiler.GenerateCode" {
#SpringGreen:compiler.GenerateCode binaryModel;
:loadGoTemplate;
:renderGoTemplate;
note right
using binaryModel data
only commands part for the moment
endnote
if (ok) then (code)
else (error)
stop
endif
}
partition "compiler.Compile" {
:compiler.extractUniqueFrameworkFunctions &functionsMap, code;
partition "compiler.retrieveEachFunctionPath" #LightSkyBlue {
:compiler.retrieveEachFunctionPath &functionsMap, binaryModel.BinFile.SrcDirs;
note right
for each function retrieve the full src file path
try to get also _.sh and ZZZ.sh files if exists
endnote
repeat :each function of functionsMap;
if (function) then (not already retrieved)
:compile.findFunctionInSrcDirs function, binaryModel.BinFile.SrcDirs;
if (file not found) then (error)
stop
endif
:register function src file;
:register _.sh if exists;
:register ZZZ.sh if exists;
endif
repeat while (more function?)
}
partition "compiler.retrieveAllFunctionsContent" #LightSkyBlue {
:compiler.retrieveAllFunctionsContent &functionsMap;
note right
for each function retrieve content of each src file path
endnote
}
partition "Compiler::Require::requires" #LightSkyBlue {
#SpringGreen:Compiler::Require::filter scriptFile;
while (requireDirective?) is (<color:green>require directive found)
-[#green]->
#SpringGreen:Compiler::Require::parse $requireDirective
~~uses Compiler::Require::assertInterface~~
;
if (implement directive can't be parsed or function does not exist?) is (<color:red>invalid directive) then
-[#red]->
end
else
-[#green]->
endif
-[#green]->
:Compiler::Require::parse;
endwhile (no more require\ndirective to process)
}
:import functions from 2 previous task
and inject them before # FUNCTIONS token;
}
-[#green,dashed]-> compiler
process
continues
;
partition "Compiler::Require::requires" #LightSkyBlue {
note right
**second phase**
call again Compiler::Require::requires
to get final list of requires in reverse order
to add the calls to those require functions
just after the token ~~# REQUIRES~~
endnote
:File::insertFileAfterToken requires "# REQUIRES";
}
partition "compilerEnds" #pink {
:Compiler::Implement::mergeInterfacesFunctions;
:Compiler::Implement::validateInterfaceFunctions;
:Output result;
}
end
@enduml
3.3. Class diagram

Source code: bash-compiler class diagram
@startuml
legend
<u><b>Legend</b></u>
Render Aggregations: true
Render Compositions: true
Render Implementations: true
Render Connections: false
Render Fields: true
Render Methods: true
Private Aggregations: true
end legend
namespace compiler {
interface AnnotationProcessorInterface {
+ Init(compileContextData *CompileContextData) error
+ ParseFunction(compileContextData *CompileContextData, functionStruct *functionInfoStruct) error
+ Process(compileContextData *CompileContextData) error
+ PostProcess(compileContextData *CompileContextData, code string) (string, error)
}
class CompileContext << (S,Aquamarine) >> {
+ Init(templateContextData *render.TemplateContextData, config *model.CompilerConfig) (*CompileContextData, error)
+ Compile(compileContextData *CompileContextData, code string) (string, error)
}
class CompileContextData << (S,Aquamarine) >> {
+ Validate() error
}
class annotation << (S,Aquamarine) >> {
}
class annotationCastError << (S,Aquamarine) >> {
+ FunctionName string
+ Error() string
}
class annotationEmbedGenerate << (S,Aquamarine) >> {
+ RenderResource(asName string, resource string, lineNumber int) (string, error)
}
interface annotationEmbedGenerateInterface {
+ RenderResource(asName string, resource string, lineNumber int) (string, error)
}
class annotationProcessor << (S,Aquamarine) >> {
}
class compiler.InsertPosition << (T, #FF7700) >> {
}
class duplicatedAsNameError << (S,Aquamarine) >> {
+ Error() string
}
class duplicatedFunctionsDirectiveError << (S,Aquamarine) >> {
+ LineNumber int
+ Error() string
}
class embedAnnotationProcessor << (S,Aquamarine) >> {
+ Init(compileContextData *CompileContextData) error
+ ParseFunction(_ *CompileContextData, _ *functionInfoStruct) error
+ Process(_ *CompileContextData) error
+ PostProcess(_ *CompileContextData, code string) (string, error)
}
class functionInfoStruct << (S,Aquamarine) >> {
+ FunctionName string
+ SrcFile string
+ SourceCode string
+ AnnotationMap <font color=blue>map</font>[string]<font color=blue>interface</font>{}
+ Inserted bool
+ InsertPosition InsertPosition
+ SourceCodeLoaded bool
+ SourceCodeAsTemplate bool
}
class functionNotFoundError << (S,Aquamarine) >> {
+ FunctionName string
+ SrcDirs []string
+ Error() string
}
class requireAnnotation << (S,Aquamarine) >> {
}
class requireAnnotationProcessor << (S,Aquamarine) >> {
+ Init(compileContextData *CompileContextData) error
+ ParseFunction(compileContextData *CompileContextData, functionStruct *functionInfoStruct) error
+ Process(compileContextData *CompileContextData) error
+ PostProcess(_ *CompileContextData, code string) (string, error)
}
class requiredFunctionNotFoundError << (S,Aquamarine) >> {
+ Error() string
}
class unsupportedEmbeddedResourceError << (S,Aquamarine) >> {
+ Error() string
}
}
"__builtin__.error" *-- "extends""compiler.annotationCastError"
"__builtin__.error" *-- "extends""compiler.duplicatedAsNameError"
"__builtin__.error" *-- "extends""compiler.duplicatedFunctionsDirectiveError"
"compiler.annotationProcessor" *-- "extends""compiler.embedAnnotationProcessor"
"__builtin__.error" *-- "extends""compiler.functionNotFoundError"
"compiler.annotation" *-- "extends""compiler.requireAnnotation"
"compiler.annotationProcessor" *-- "extends""compiler.requireAnnotationProcessor"
"__builtin__.error" *-- "extends""compiler.requiredFunctionNotFoundError"
"__builtin__.error" *-- "extends""compiler.unsupportedEmbeddedResourceError"
"services.CodeCompilerInterface" <|-- "implements""compiler.CompileContext"
"compiler.annotationEmbedGenerateInterface" <|-- "implements""compiler.annotationEmbedGenerate"
"compiler.AnnotationProcessorInterface" <|-- "implements""compiler.embedAnnotationProcessor"
"compiler.AnnotationProcessorInterface" <|-- "implements""compiler.requireAnnotationProcessor"
"compiler.CompileContext""uses" o-- "compiler.AnnotationProcessorInterface"
"compiler.CompileContext""uses" o-- "render.TemplateContextInterface"
"compiler.CompileContextData""uses" o-- "compiler.CompileContext"
"compiler.CompileContextData""uses" o-- "compiler.functionInfoStruct"
"compiler.CompileContextData""uses" o-- "model.CompilerConfig"
"compiler.CompileContextData""uses" o-- "regexp.Regexp"
"compiler.CompileContextData""uses" o-- "render.TemplateContextData"
"compiler.annotationEmbedGenerate""uses" o-- "render.TemplateContextData"
"compiler.embedAnnotationProcessor""uses" o-- "compiler.annotationEmbedGenerateInterface"
"compiler.functionInfoStruct""uses" o-- "compiler.InsertPosition"
"compiler.requireAnnotationProcessor""uses" o-- "compiler.CompileContextData"
namespace errors {
class ValidationError << (S,Aquamarine) >> {
+ InnerError error
+ Context string
+ FieldName string
+ FieldValue any
+ Error() string
}
}
"errors.ValidationError""uses" o-- "errors.any"
namespace files {
class directoryPathMissingError << (S,Aquamarine) >> {
+ DirPath string
+ Error() string
}
class directoryWasExpectedError << (S,Aquamarine) >> {
+ Directory string
+ Error() string
}
class filePathMissingError << (S,Aquamarine) >> {
+ FilePath string
+ Error() string
}
class fileWasExpectedError << (S,Aquamarine) >> {
+ File string
+ Error() string
}
}
"__builtin__.error" *-- "extends""files.directoryPathMissingError"
"__builtin__.error" *-- "extends""files.directoryWasExpectedError"
"__builtin__.error" *-- "extends""files.filePathMissingError"
"__builtin__.error" *-- "extends""files.fileWasExpectedError"
namespace main {
class Directory << (S,Aquamarine) >> {
+ Validate() error
}
class VersionFlag << (S,Aquamarine) >> {
+ Decode(_ *kong.DecodeContext) error
+ IsBool() bool
+ BeforeApply(app *kong.Kong, vars kong.Vars) error
}
class YamlFiles << (S,Aquamarine) >> {
+ Validate() error
}
class cli << (S,Aquamarine) >> {
+ YamlFiles YamlFiles
+ TargetDir Directory
+ Version VersionFlag
+ KeepIntermediateFiles bool
+ Debug bool
+ LogLevel int
+ CompilerRootDir Directory
}
class getCurrentFilenameError << (S,Aquamarine) >> {
+ Error() string
}
class main.Directory << (T, #FF7700) >> {
}
class main.VersionFlag << (T, #FF7700) >> {
}
class main.YamlFiles << (T, #FF7700) >> {
}
}
"__builtin__.error" *-- "extends""main.getCurrentFilenameError"
"main.cli""uses" o-- "main.Directory"
"main.cli""uses" o-- "main.VersionFlag"
"main.cli""uses" o-- "main.YamlFiles"
namespace model {
class BinaryModel << (S,Aquamarine) >> {
+ CompilerConfig CompilerConfig
+ Vars structures.Dictionary
+ BinData <font color=blue>interface</font>{}
}
class BinaryModelLoader << (S,Aquamarine) >> {
+ Load(targetDir string, binaryModelFilePath string, binaryModelBaseName string, referenceDir string, keepIntermediateFiles bool) (*BinaryModel, error)
}
class CompilerConfig << (S,Aquamarine) >> {
+ AnnotationsConfig structures.Dictionary
+ TargetFile string
+ RelativeRootDirBasedOnTargetDir string
+ CommandDefinitionFiles []string
+ TemplateFile string
+ TemplateDirs []string
+ FunctionsIgnoreRegexpList []string
+ SrcDirs []string
+ SrcDirsExpanded []string
}
}
"services.BinaryModelLoaderInterface" <|-- "implements""model.BinaryModelLoader"
"model.BinaryModel""uses" o-- "model.CompilerConfig"
"model.BinaryModel""uses" o-- "structures.Dictionary"
"model.CompilerConfig""uses" o-- "structures.Dictionary"
namespace render {
class TemplateContext << (S,Aquamarine) >> {
+ Init(templateDirs []string, templateFile string, data <font color=blue>interface</font>{}, funcMap <font color=blue>map</font>[string]<font color=blue>interface</font>{}) (*TemplateContextData, error)
+ Render(templateContextData *TemplateContextData, templateName string) (string, error)
+ RenderFromTemplateName(templateContextData *TemplateContextData) (string, error)
+ RenderFromTemplateContent(templateContextData *TemplateContextData, templateContent string) (string, error)
}
class TemplateContextData << (S,Aquamarine) >> {
+ TemplateContext TemplateContextInterface
+ TemplateName *string
+ Template templateInterface
+ RootData <font color=blue>interface</font>{}
+ Data <font color=blue>interface</font>{}
}
interface TemplateContextInterface {
+ Render(templateContextData *TemplateContextData, templateName string) (string, error)
+ RenderFromTemplateContent(templateContextData *TemplateContextData, templateContent string) (string, error)
}
class fileNotFoundError << (S,Aquamarine) >> {
+ File string
+ SrcDirs []string
+ Error() string
}
class notSupportedTypeError << (S,Aquamarine) >> {
+ ObjectType string
+ Error() string
}
interface templateInterface {
+ ExecuteTemplate(wr io.Writer, name string, data any) error
+ Parse(text string) (*template.Template, error)
}
}
"__builtin__.error" *-- "extends""render.fileNotFoundError"
"__builtin__.error" *-- "extends""render.notSupportedTypeError"
"render.TemplateContextInterface" <|-- "implements""render.TemplateContext"
"services.TemplateContextInterface" <|-- "implements""render.TemplateContext"
"render.TemplateContextData""uses" o-- "render.TemplateContextInterface"
"render.TemplateContextData""uses" o-- "render.templateInterface"
namespace services {
interface BinaryModelLoaderInterface {
+ Load(targetDir string, binaryModelFilePath string, binaryModelBaseName string, referenceDir string, keepIntermediateFiles bool) (*model.BinaryModel, error)
}
class BinaryModelServiceContext << (S,Aquamarine) >> {
+ Init(targetDir string, keepIntermediateFiles bool, binaryModelFilePath string) (*BinaryModelServiceContextData, error)
+ Compile(binaryModelServiceContextData *BinaryModelServiceContextData) error
}
class BinaryModelServiceContextData << (S,Aquamarine) >> {
}
interface CodeCompilerInterface {
+ Init(templateContextData *render.TemplateContextData, config *model.CompilerConfig) (*compiler.CompileContextData, error)
+ Compile(compileContextData *compiler.CompileContextData, code string) (string, error)
}
interface TemplateContextInterface {
+ Init(templateDirs []string, templateFile string, data <font color=blue>interface</font>{}, funcMap <font color=blue>map</font>[string]<font color=blue>interface</font>{}) (*render.TemplateContextData, error)
+ Render(templateContextData *render.TemplateContextData, templateName string) (string, error)
+ RenderFromTemplateName(templateContextData *render.TemplateContextData) (string, error)
+ RenderFromTemplateContent(templateContextData *render.TemplateContextData, templateContent string) (string, error)
}
}
"services.BinaryModelServiceContext""uses" o-- "services.BinaryModelLoaderInterface"
"services.BinaryModelServiceContext""uses" o-- "services.CodeCompilerInterface"
"services.BinaryModelServiceContext""uses" o-- "services.TemplateContextInterface"
"services.BinaryModelServiceContextData""uses" o-- "compiler.CompileContextData"
"services.BinaryModelServiceContextData""uses" o-- "model.BinaryModel"
"services.BinaryModelServiceContextData""uses" o-- "render.TemplateContextData"
namespace structures {
class Dictionary << (S,Aquamarine) >> {
+ GetStringValue(key string) (string, error)
+ GetStringList(key string) ([]string, error)
}
class invalidValueTypeError << (S,Aquamarine) >> {
+ Value any
+ Error() string
}
class missingKeyError << (S,Aquamarine) >> {
+ Key string
+ Error() string
}
class structures.Dictionary << (T, #FF7700) >> {
}
}
"__builtin__.error" *-- "extends""structures.invalidValueTypeError"
"__builtin__.error" *-- "extends""structures.missingKeyError"
"structures.invalidValueTypeError""uses" o-- "structures.any"
@enduml
Source code: bash-compiler class diagram with private methods
@startuml
legend
<u><b>Legend</b></u>
Render Aggregations: true
Render Fields: true
Render Methods: true
Private Aggregations: true
end legend
namespace compiler {
interface AnnotationProcessorInterface {
+ Init(compileContextData *CompileContextData) error
+ ParseFunction(compileContextData *CompileContextData, functionStruct *functionInfoStruct) error
+ Process(compileContextData *CompileContextData) error
+ PostProcess(compileContextData *CompileContextData, code string) (string, error)
}
class CompileContext << (S,Aquamarine) >> {
- templateContext render.TemplateContextInterface
- annotationProcessors []AnnotationProcessorInterface
- generateCode(compileContextData *CompileContextData, code string) (bool, string, error)
- functionsAnalysis(compileContextData *CompileContextData, code string) error
- renderEachFunctionAsTemplate(compileContextData *CompileContextData) error
- isNonFrameworkFunction(compileContextData *CompileContextData, functionName string) bool
- nonFrameworkFunctionRegexpCompile(compileContextData *CompileContextData)
- generateFunctionCode(compileContextData *CompileContextData) (string, error)
- insertFunctionsCode(compileContextData *CompileContextData, functionNames []string, buffer *bytes.Buffer, insertPosition InsertPosition) error
- retrieveAllFunctionsContent(compileContextData *CompileContextData) (bool, error)
- retrieveEachFunctionPath(compileContextData *CompileContextData) (bool, error)
- extractUniqueFrameworkFunctions(compileContextData *CompileContextData, code string) bool
- findFileInSrcDirs(compileContextData *CompileContextData, relativeFilePath string) (string, bool)
+ Init(templateContextData *render.TemplateContextData, config *model.CompilerConfig) (*CompileContextData, error)
+ Compile(compileContextData *CompileContextData, code string) (string, error)
}
class CompileContextData << (S,Aquamarine) >> {
- compileContext *CompileContext
- templateContextData *render.TemplateContextData
- config *model.CompilerConfig
- functionsMap <font color=blue>map</font>[string]functionInfoStruct
- ignoreFunctionsRegexp []*regexp.Regexp
+ Validate() error
}
class annotation << (S,Aquamarine) >> {
}
class annotationCastError << (S,Aquamarine) >> {
+ FunctionName string
+ Error() string
}
class annotationEmbedGenerate << (S,Aquamarine) >> {
- embedDirTemplateName string
- embedFileTemplateName string
- templateContextData *render.TemplateContextData
- renderFile(asName string, resource string, fileMode os.FileMode) (string, error)
- renderDir(asName string, resource string) (string, error)
- renderTemplate(data <font color=blue>map</font>[string]string, templateName string) (string, error)
+ RenderResource(asName string, resource string, lineNumber int) (string, error)
}
interface annotationEmbedGenerateInterface {
+ RenderResource(asName string, resource string, lineNumber int) (string, error)
}
class annotationProcessor << (S,Aquamarine) >> {
}
class compiler.InsertPosition << (T, #FF7700) >> {
}
class duplicatedAsNameError << (S,Aquamarine) >> {
- lineNumber int
- asName string
- resource string
+ Error() string
}
class duplicatedFunctionsDirectiveError << (S,Aquamarine) >> {
+ LineNumber int
+ Error() string
}
class embedAnnotationProcessor << (S,Aquamarine) >> {
- annotationEmbedGenerate annotationEmbedGenerateInterface
- embedMap <font color=blue>map</font>[string]string
+ Init(compileContextData *CompileContextData) error
+ ParseFunction(_ *CompileContextData, _ *functionInfoStruct) error
+ Process(_ *CompileContextData) error
+ PostProcess(_ *CompileContextData, code string) (string, error)
}
class functionInfoStruct << (S,Aquamarine) >> {
+ FunctionName string
+ SrcFile string
+ SourceCode string
+ AnnotationMap <font color=blue>map</font>[string]<font color=blue>interface</font>{}
+ Inserted bool
+ InsertPosition InsertPosition
+ SourceCodeLoaded bool
+ SourceCodeAsTemplate bool
- getRequireAnnotation() (*requireAnnotation, error)
}
class functionNotFoundError << (S,Aquamarine) >> {
+ FunctionName string
+ SrcDirs []string
+ Error() string
}
class requireAnnotation << (S,Aquamarine) >> {
- requiredFunctions []string
- isRequired bool
- checkRequirementsCodeAdded bool
- codeAddedOnRequiredFunctions bool
}
class requireAnnotationProcessor << (S,Aquamarine) >> {
- compileContextData *CompileContextData
- checkRequirementsTemplateName string
- requireTemplateName string
- addRequireCodeToEachRequiredFunctions(compileContextData *CompileContextData, functionStruct *functionInfoStruct) error
- addRequireCode(compileContextData *CompileContextData, functionStruct *functionInfoStruct) error
+ Init(compileContextData *CompileContextData) error
+ ParseFunction(compileContextData *CompileContextData, functionStruct *functionInfoStruct) error
+ Process(compileContextData *CompileContextData) error
+ PostProcess(_ *CompileContextData, code string) (string, error)
}
class requiredFunctionNotFoundError << (S,Aquamarine) >> {
- functionName string
+ Error() string
}
class unsupportedEmbeddedResourceError << (S,Aquamarine) >> {
- asName string
- resource string
- lineNumber int
+ Error() string
}
}
"__builtin__.error" *-- "extends""compiler.annotationCastError"
"__builtin__.error" *-- "extends""compiler.duplicatedAsNameError"
"__builtin__.error" *-- "extends""compiler.duplicatedFunctionsDirectiveError"
"compiler.annotationProcessor" *-- "extends""compiler.embedAnnotationProcessor"
"__builtin__.error" *-- "extends""compiler.functionNotFoundError"
"compiler.annotation" *-- "extends""compiler.requireAnnotation"
"compiler.annotationProcessor" *-- "extends""compiler.requireAnnotationProcessor"
"__builtin__.error" *-- "extends""compiler.requiredFunctionNotFoundError"
"__builtin__.error" *-- "extends""compiler.unsupportedEmbeddedResourceError"
"services.CodeCompilerInterface" <|-- "implements""compiler.CompileContext"
"compiler.annotationEmbedGenerateInterface" <|-- "implements""compiler.annotationEmbedGenerate"
"compiler.AnnotationProcessorInterface" <|-- "implements""compiler.embedAnnotationProcessor"
"compiler.AnnotationProcessorInterface" <|-- "implements""compiler.requireAnnotationProcessor"
"compiler.CompileContext""uses" o-- "compiler.AnnotationProcessorInterface"
"compiler.CompileContext""uses" o-- "render.TemplateContextInterface"
"compiler.CompileContextData""uses" o-- "compiler.CompileContext"
"compiler.CompileContextData""uses" o-- "compiler.functionInfoStruct"
"compiler.CompileContextData""uses" o-- "model.CompilerConfig"
"compiler.CompileContextData""uses" o-- "regexp.Regexp"
"compiler.CompileContextData""uses" o-- "render.TemplateContextData"
"compiler.annotationEmbedGenerate""uses" o-- "render.TemplateContextData"
"compiler.embedAnnotationProcessor""uses" o-- "compiler.annotationEmbedGenerateInterface"
"compiler.functionInfoStruct""uses" o-- "compiler.InsertPosition"
"compiler.requireAnnotationProcessor""uses" o-- "compiler.CompileContextData"
namespace errors {
class ValidationError << (S,Aquamarine) >> {
+ InnerError error
+ Context string
+ FieldName string
+ FieldValue any
+ Error() string
}
}
"errors.ValidationError""uses" o-- "errors.any"
namespace files {
class directoryPathMissingError << (S,Aquamarine) >> {
+ DirPath string
+ Error() string
}
class directoryWasExpectedError << (S,Aquamarine) >> {
+ Directory string
+ Error() string
}
class filePathMissingError << (S,Aquamarine) >> {
+ FilePath string
+ Error() string
}
class fileWasExpectedError << (S,Aquamarine) >> {
+ File string
+ Error() string
}
}
"__builtin__.error" *-- "extends""files.directoryPathMissingError"
"__builtin__.error" *-- "extends""files.directoryWasExpectedError"
"__builtin__.error" *-- "extends""files.filePathMissingError"
"__builtin__.error" *-- "extends""files.fileWasExpectedError"
namespace main {
class Directory << (S,Aquamarine) >> {
+ Validate() error
}
class VersionFlag << (S,Aquamarine) >> {
+ Decode(_ *kong.DecodeContext) error
+ IsBool() bool
+ BeforeApply(app *kong.Kong, vars kong.Vars) error
}
class YamlFiles << (S,Aquamarine) >> {
+ Validate() error
}
class cli << (S,Aquamarine) >> {
+ YamlFiles YamlFiles
+ TargetDir Directory
+ Version VersionFlag
+ KeepIntermediateFiles bool
+ Debug bool
+ LogLevel int
+ CompilerRootDir Directory
}
class getCurrentFilenameError << (S,Aquamarine) >> {
+ Error() string
}
class main.Directory << (T, #FF7700) >> {
}
class main.VersionFlag << (T, #FF7700) >> {
}
class main.YamlFiles << (T, #FF7700) >> {
}
}
"__builtin__.error" *-- "extends""main.getCurrentFilenameError"
"main.cli""uses" o-- "main.Directory"
"main.cli""uses" o-- "main.VersionFlag"
"main.cli""uses" o-- "main.YamlFiles"
namespace model {
class BinaryModel << (S,Aquamarine) >> {
+ CompilerConfig CompilerConfig
+ Vars structures.Dictionary
+ BinData <font color=blue>interface</font>{}
}
class BinaryModelLoader << (S,Aquamarine) >> {
- setEnvVars(binaryModel *BinaryModel)
- expandVars(binaryModel *BinaryModel)
+ Load(targetDir string, binaryModelFilePath string, binaryModelBaseName string, referenceDir string, keepIntermediateFiles bool) (*BinaryModel, error)
}
class CompilerConfig << (S,Aquamarine) >> {
+ AnnotationsConfig structures.Dictionary
+ TargetFile string
+ RelativeRootDirBasedOnTargetDir string
+ CommandDefinitionFiles []string
+ TemplateFile string
+ TemplateDirs []string
+ FunctionsIgnoreRegexpList []string
+ SrcDirs []string
+ SrcDirsExpanded []string
}
}
"services.BinaryModelLoaderInterface" <|-- "implements""model.BinaryModelLoader"
"model.BinaryModel""uses" o-- "model.CompilerConfig"
"model.BinaryModel""uses" o-- "structures.Dictionary"
"model.CompilerConfig""uses" o-- "structures.Dictionary"
namespace render {
class TemplateContext << (S,Aquamarine) >> {
+ Init(templateDirs []string, templateFile string, data <font color=blue>interface</font>{}, funcMap <font color=blue>map</font>[string]<font color=blue>interface</font>{}) (*TemplateContextData, error)
+ Render(templateContextData *TemplateContextData, templateName string) (string, error)
+ RenderFromTemplateName(templateContextData *TemplateContextData) (string, error)
+ RenderFromTemplateContent(templateContextData *TemplateContextData, templateContent string) (string, error)
}
class TemplateContextData << (S,Aquamarine) >> {
+ TemplateContext TemplateContextInterface
+ TemplateName *string
+ Template templateInterface
+ RootData <font color=blue>interface</font>{}
+ Data <font color=blue>interface</font>{}
}
interface TemplateContextInterface {
+ Render(templateContextData *TemplateContextData, templateName string) (string, error)
+ RenderFromTemplateContent(templateContextData *TemplateContextData, templateContent string) (string, error)
}
class fileNotFoundError << (S,Aquamarine) >> {
+ File string
+ SrcDirs []string
+ Error() string
}
class notSupportedTypeError << (S,Aquamarine) >> {
+ ObjectType string
+ Error() string
}
interface templateInterface {
+ ExecuteTemplate(wr io.Writer, name string, data any) error
+ Parse(text string) (*template.Template, error)
}
}
"__builtin__.error" *-- "extends""render.fileNotFoundError"
"__builtin__.error" *-- "extends""render.notSupportedTypeError"
"render.TemplateContextInterface" <|-- "implements""render.TemplateContext"
"services.TemplateContextInterface" <|-- "implements""render.TemplateContext"
"render.TemplateContextData""uses" o-- "render.TemplateContextInterface"
"render.TemplateContextData""uses" o-- "render.templateInterface"
namespace services {
interface BinaryModelLoaderInterface {
+ Load(targetDir string, binaryModelFilePath string, binaryModelBaseName string, referenceDir string, keepIntermediateFiles bool) (*model.BinaryModel, error)
}
class BinaryModelServiceContext << (S,Aquamarine) >> {
- binaryModelLoader BinaryModelLoaderInterface
- templateContext TemplateContextInterface
- codeCompiler CodeCompilerInterface
- renderBinaryCodeFromTemplate(binaryModelServiceContextData *BinaryModelServiceContextData) (string, error)
- renderCode(binaryModelServiceContextData *BinaryModelServiceContextData) (string, error)
+ Init(targetDir string, keepIntermediateFiles bool, binaryModelFilePath string) (*BinaryModelServiceContextData, error)
+ Compile(binaryModelServiceContextData *BinaryModelServiceContextData) error
}
class BinaryModelServiceContextData << (S,Aquamarine) >> {
- binaryModelData *model.BinaryModel
- compileContextData *compiler.CompileContextData
- templateContextData *render.TemplateContextData
- targetDir string
- keepIntermediateFiles bool
- binaryModelFilePath string
- binaryModelBaseName string
}
interface CodeCompilerInterface {
+ Init(templateContextData *render.TemplateContextData, config *model.CompilerConfig) (*compiler.CompileContextData, error)
+ Compile(compileContextData *compiler.CompileContextData, code string) (string, error)
}
interface TemplateContextInterface {
+ Init(templateDirs []string, templateFile string, data <font color=blue>interface</font>{}, funcMap <font color=blue>map</font>[string]<font color=blue>interface</font>{}) (*render.TemplateContextData, error)
+ Render(templateContextData *render.TemplateContextData, templateName string) (string, error)
+ RenderFromTemplateName(templateContextData *render.TemplateContextData) (string, error)
+ RenderFromTemplateContent(templateContextData *render.TemplateContextData, templateContent string) (string, error)
}
}
"services.BinaryModelServiceContext""uses" o-- "services.BinaryModelLoaderInterface"
"services.BinaryModelServiceContext""uses" o-- "services.CodeCompilerInterface"
"services.BinaryModelServiceContext""uses" o-- "services.TemplateContextInterface"
"services.BinaryModelServiceContextData""uses" o-- "compiler.CompileContextData"
"services.BinaryModelServiceContextData""uses" o-- "model.BinaryModel"
"services.BinaryModelServiceContextData""uses" o-- "render.TemplateContextData"
namespace structures {
class Dictionary << (S,Aquamarine) >> {
+ GetStringValue(key string) (string, error)
+ GetStringList(key string) ([]string, error)
}
class invalidValueTypeError << (S,Aquamarine) >> {
+ Value any
+ Error() string
}
class missingKeyError << (S,Aquamarine) >> {
+ Key string
+ Error() string
}
class structures.Dictionary << (T, #FF7700) >> {
}
}
"__builtin__.error" *-- "extends""structures.invalidValueTypeError"
"__builtin__.error" *-- "extends""structures.missingKeyError"
"structures.invalidValueTypeError""uses" o-- "structures.any"
"__builtin__.[]string" #.. "alias of""main.YamlFiles"
"__builtin__.int8" #.. "alias of""compiler.InsertPosition"
"__builtin__.string" #.. "alias of""main.Directory"
"__builtin__.string" #.. "alias of""main.VersionFlag"
"structures.<font color=blue>map</font>[string]<font color=blue>interface</font>{}" #.. "alias of""structures.Dictionary"
@enduml
3.4. Dependency diagram

Source code: bash-compiler dependency diagram
@startuml
'!pragma layout elk
package main {
[Main] ..> ParseCliInterface
[Main] ..> BinaryModelServiceInterface
ParseCliInterface )-- [ParseCli] : <<implements>>
}
package services {
BinaryModelServiceInterface )-- [BinaryModelService] : <<implements>>
[BinaryModelService] ..> BinaryModelInterface : <<uses>>
[BinaryModelService] ..> TemplateContextInterface : <<uses>>
[BinaryModelService] ..> CompilerInterface : <<uses>>
}
package model {
BinaryModelInterface )-- [BinaryModel] : <<implements>>
}
package render {
TemplateContextInterface )-- [TemplateContext] : <<implements>>
}
package compiler {
CompilerInterface )-- [Compiler] : <<implements>>
}
@enduml
3.5. directives and template
You can use special optional directives in src file
# FUNCTIONSmandatory directive@embeddirective@requiredirective
Compile command allows to generate a binary file using some directives directly inside the src file.
Eg:
#!/usr/bin/env bash
# BIN_FILE=${FRAMEWORK_ROOT_DIR}/bin/binaryExample
# @embed "Backup::file" as backupFile
# @embed "${FRAMEWORK_ROOT_DIR}/bin/otherNeededBinary" AS "otherNeededBinary"
# FACADE
sudo "${embed_file_backupFile}" # ...
"${embed_file_otherNeededBinary}"
# ...
The above file header allows to generate the bin/binaryExample binary file. It uses @embed directive to allow the
usage of Backup::file function as a binary, named backupFile that can even be called using sudo.
In previous example, the directive # FUNCTIONS is injected via the file
cmd/bash-compiler/defaultTemplates/binFile.gtpl.
The srcFile should contains at least the directive BIN_FILE at top of the bash script file (see example above).
3.5.1. FUNCTIONS directive
It is the most important directive as it will inform the compiler where dependent framework functions will be injected in your resulting bash file.
3.5.2. Compiler - Compiler::Requirement::require
The compiler during successive passes:
- use existing compiler passes (injectImportedFunctions)
- will parse
# @requiredirectives of each newly injected functions- error if require name does not begin with require
- error if require name does not comply naming convention
- error if
require*file not found
- will ignore the disabled requirements
- a tree of require dependencies will be computed
- we inject gradually the framework functions linked to the requires functions
- will parse
- At the end of compiler processing
- inject the requirements calls in the order specified by dependency tree (see below).
3.5.2.1. Requires dependencies tree
The following rules apply:
- Some requirements can depends on each others, the compiler will compute which dependency should be loaded before the other. Eg: Log::requireLoad requirement depends on Framework::requireRootDir, so Framework::requireRootDir is loaded before. But Log requirement depends also on Env::requireLoad requirement.
- Requirement can be set at namespace level by adding the directive in
_.shfile or at function level. - A requirement can be loaded only once.
- A requirement that is used by several functions will be more prioritized and will be loaded before a less prioritized requirement.
# FUNCTIONSplaceholder should be defined before# REQUIREMENTSplaceholder# REQUIREMENTSplaceholder should be defined before# ENTRYPOINTplaceholder
3.5.2.2. Require example
The annotation @require added to a function like in this example:
# @require Env::requireLoad
# @require Log::requireLoad
Log::logMessage() {
# rest of the function content
}
will do the following actions:
- compiler checks that the required functions exist, if not an error is triggered.
- compiler adds code to the required function that will set an environment variable to 1 when the function is called (eg: REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1).
- compiler adds code to the function that has these requirements in to check if these environment variables are set and exit 1 if not.
- compiler checks if the function is called at least once but it is the developer’s responsibility to call the require function at the right place.
Code is generated using go templates. The go templates are configured in the yaml file at compiler config level.
compilerConfig:
annotationsConfig:
requireTemplate: require
checkRequirementsTemplate: checkRequirements
# rest of the config file content
examples/templates/annotations/require.gtpl => generates this code:
Env::RequireLoad() {
REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1
# rest of the function content
}
examples/templates/annotations/checkRequirements.gtpl => generates this code:
# @require Env::requireLoad
# @require Log::requireLoad
Log::logMessage() {
if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then
echo >&2 "Requirement Env::requireLoad has not been loaded"
exit 1
fi
if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then
echo >&2 "Requirement Log::requireLoad has not been loaded"
exit 1
fi
# rest of the function content
}
The aims of a require are the following:
- be to be able to test for a requirement just before executing a function that is marked with @require
- when compiling be able to know if a function with a specific requirement has been used (eg: ubuntu>20)
- There are several kind of requirements:
- checking that a command is available
- this requirement needs to be called at the proper level if the binary actually installs this command.
- @require Aws::requireAwsCommand
- @require Docker::requireDockerCommand
- @require Git::requireGitCommand
- @require Linux::requireCurlCommand
- @require Linux::requireJqCommand
- @require Linux::requireRealpathCommand
- @require Linux::requirePathchkCommand
- @require Linux::requireSudoCommand
- @require Linux::requireTarCommand
- @require Ssh::requireSshKeygenCommand
- @require Ssh::requireSshKeyscanCommand
- checking a feature is available
- @require Git::requireShallowClone actually based on git version
- checking a specific environment/state is available on execution
- @require Linux::requireUbuntu
- @require Linux::Wsl::requireWsl
- @require Linux::requireExecutedAsUser
- ubuntu>20
- ensuring some specific loading are made
- @require Env::requireLoad
- @require Log::requireLoad
- @require UI::requireTheme
- checking that a command is available
3.5.2.3. Requires dependencies use cases
Script file example:
# FUNCTIONS placeholder
# REQUIRES placeholder
Linux::Apt::update || Log::displayError "impossible to update"
- first compiler injectImportedFunctions pass
Linux::Apt::updaterequiresLinux::requireSudoCommandLinux::requireUbuntu
Log::display*requiresColors::requireTheme
- second compiler injectImportedFunctions pass
Log::log*requiresLog::requireLoad
- third compiler injectImportedFunctions pass
Log::requireLoadrequiresEnv::requireLoad
- fourth compiler injectImportedFunctions pass
Env::requireLoadrequiresFramework::requireRootDirFramework::tmpFileManagement(seesrc/_includes/_commonHeader.sh)
- fifth compiler injectImportedFunctions pass
Framework::tmpFileManagementrequiresFramework::requireRootDirwhich is already in the required list
If we order the requirements following reversed pass order, we end up with:
Framework::tmpFileManagementFramework::requireRootDir- here we have an issue as it should come before
Framework::tmpFileManagement - a solution could be to add the element to require list even if it is already in the list. This way it could even give a weight at certain requires.
- here we have an issue as it should come before
Env::requireLoadLog::requireLoadColors::requireThemeLinux::requireUbuntuLinux::requireSudoCommand
To take into consideration:
- at each pass, we will parse the full list of functions and requires
- it means the array of requires has to be reset at each pass.
Let’s take again our above example, pass by pass (we avoided to include some functions intentionally like
Retry:default needed by Linux::Apt::update to make example easier to understand).
Pass #1: import functions Linux::Apt::update and Log::displayError
# @require Linux::requireSudoCommand
# @require Linux::requireUbuntu
Linux::Apt::update() { :; }
# @require Log::requireLoad
Log::displayError() {
#...
Log:logMessage #...
}
# FUNCTIONS placeholder
# we don't have any yet as we are still parsing the 3 lines
# code above.
# REQUIRES placeholder
Linux::Apt::update || Log::displayError "impossible to update"
Functions imported list so far:
- Linux::Apt::update
- Log::displayError
Pass #2: import functions Log:logMessage and import required functions in reverse order Linux::requireSudoCommand,
Linux::requireUbuntu, Log::requireLoad Note: remember that require functions are only filtered using # @require
# @require Linux::requireSudoCommand
# @require Linux::requireUbuntu
Linux::Apt::update() { :; }
# @require Log::requireLoad
Log::displayError() {
#...
Log:logMessage #...
}
Log:logMessage() { :; }
Linux::requireSudoCommand() { :; }
Linux::requireUbuntu() { :; }
# @require Env::requireLoad
Log::requireLoad() { :; }
# FUNCTIONS placeholder
Log::requireLoad
Linux::requireUbuntu
Linux::requireSudoCommand
# REQUIRES placeholder
Linux::Apt::update || Log::displayError "impossible to update"
Functions imported list so far:
- Linux::Apt::update
- Log::displayError
- Log:logMessage
- Log::requireLoad
- Linux::requireSudoCommand
- Linux::requireUbuntu
Pass #3: import functions, import required functions will import Env::requireLoad so order of requires will be:
Env:requireLoad
Log::requireLoad
Linux::requireUbuntu
Linux::requireSudoCommand
3.5.3. @embed directive (optional)
Allows to embed files, directories or a framework function. The following syntax can be used:
Syntax: # @embed "srcFile" AS "targetFile"
Syntax: # @embed "srcDir" AS "targetDir"
if @embed annotation is provided, the file/dir provided will be added inside the resulting bin file as a tar gz
file(base64 encoded) and automatically extracted when executed.
The @embed annotation allows to embed as base64 encoded a file or a directory. annotationEmbed allows to:
- include a file(binary or not) as base64 encoded, the file can then be extracted using the automatically generated
method
Compiler::Embed::extractFile_asNamewhere asName is the name chosen using annotation explained above. The original file mode will be restored after extraction. The variableembed_file_asNamecontains the targeted filepath. - include a directory, the directory will be tar gz and added to the compiled file as base64 encoded string. The
directory can then be extracted using the automatically generated method
Compiler::Embed::extractDir_asNamewhere asName is the name chosen using annotation explained above. The variable embed_dir_asName contains the targeted directory path. - include a bash framework function, a special binary file that simply calls this function will be automatically
generated. This binary file will be added to the compiled file as base64 encoded string. Then it will be automatically
extracted to temporary directory and is callable directly using
asNamechosen above because path of the temporary directory has been added into the PATH variable.
The syntax is the following:
# @embed "${FRAMEWORK_ROOT_DIR}/README.md" as readme
# @embed "${FRAMEWORK_ROOT_DIR}/.cspell" as cspell
This will generate the code below:
Compiler::Embed::extractFileFromBase64 \
"${PERSISTENT_TMPDIR:-/tmp}/1e26600f34bdaf348803250aa87f4924/readme" \
"base64 encode string" \
"644"
declare -gx embed_file_readme="${PERSISTENT_TMPDIR:-/tmp}/1e26600f34bdaf348803250aa87f4924/readme"
Compiler::Embed::extractDirFromBase64 \
"${PERSISTENT_TMPDIR:-/tmp}/5c12a039d61ab2c98111e5353362f380/cspell" \
"base64 encode string"
declare -gx embed_dir_cspell="${PERSISTENT_TMPDIR:-/tmp}/5c12a039d61ab2c98111e5353362f380/cspell"
The embedded files will be automatically uncompressed.
Source code: Activity diagram
@startuml "compilerEmbedInjection"
title compiler embed injection
skinparam {
' https://github.com/plantuml/plantuml/blob/49115dfc7d4156961e5b49a81c09b474daa79823/src/net/sourceforge/plantuml/style/FromSkinparamToStyle.java#L145
activityDiamondBackgroundColor #AAAAAA
activityEndColor #red
}
start
:compiler;
#SpringGreen:Compiler::Embed::inject fromFile;
#SpringGreen:Compiler::Embed::filter fromFile;
while (embedDirective?) is (<color:green>embed found)
-[#green]->
partition Compiler::Embed::embed #SpringGreen {
#SpringGreen:Compiler::Embed::parse $embedDirective
~~uses Compiler::Embed::assertAsName~~
~~uses Compiler::Embed::assertResource~~
;
if (embed directive can be parsed?) is (<color:green>valid directive) then
-[#green]->
else (<color:red>invalid embed\n<color:red>directive)
-[#red]->
end
endif
-[#green]->
}
if (embed name already injected?) then (yes)
:display skip;
else (<color:green>embed resource can be injected)
if (embed resources already injected?) then (yes)
:display warning;
endif
partition Compiler::Embed::embed #LightGray {
#SpringGreen:__Compiler::Embed::embed__ **$resource** **$asName**;
switch (resource type?)
case ( resource is a file )
#SpringGreen:Compiler::Embed::embedFile;
case ( resource is a directory )
#SpringGreen:Compiler::Embed::embedDir;
case ( resource is a bash\nframework function )
#SpringGreen:Compiler::Embed::embedFrameworkFunction;
#SpringGreen:bin/compile;
case ( <color:red>error )
-[#red]->
end
endswitch
}
if (embed resource injection failure ?) then
-[#red]-> <color:red>display error\n<color:red>and return 2;
end
else
-[#green]->
endif
endif
-[#green]-> <color:green>process next embed directive;
endwhile (no more embed directive to process)
:Output result;
stop
@enduml
See compiler - Compiler::Embed::embed below for more information.
4. Best practices
@embed keyword is really useful to inline configuration files. However to run framework function using sudo, it is
recommended to call the same binary but passing options to change the behavior. This way the content of the script file
does not seem to be obfuscated.
5. Acknowledgements
I want to thank a lot Michał Zieliński(Tratif company) for this wonderful article that helped me a lot in the conception of the file/dir/framework function embedding feature.
for more information see Bash Tips #6 – Embedding Files In A Single Bash Script