├── .gitattributes ├── example ├── pch │ ├── mypch.h │ ├── mypch.cpp │ ├── main.cpp │ └── Marefile └── helloworld │ ├── main.cpp │ └── Marefile ├── src ├── libmare │ ├── Tools │ │ ├── String.cpp │ │ ├── Error.h │ │ ├── Assert.h │ │ ├── Scope.h │ │ ├── Error.cpp │ │ ├── Scope.cpp │ │ ├── File.h │ │ ├── Word.h │ │ ├── Process.h │ │ ├── Directory.h │ │ ├── Array.h │ │ ├── String.h │ │ ├── Map.h │ │ ├── Word.cpp │ │ ├── List.h │ │ ├── File.cpp │ │ └── Directory.cpp │ ├── Token.h │ ├── Engine.h │ ├── Parser.h │ ├── Namespace.h │ ├── Statement.h │ ├── Statement.cpp │ ├── Engine.cpp │ └── Parser.cpp └── mare │ ├── Tools │ ├── md5.h │ ├── Win32 │ │ └── getopt.h │ └── md5.cpp │ ├── JsonDb.h │ ├── Mare.h │ ├── CMake.h │ ├── CodeLite.h │ ├── NetBeans.h │ ├── Generator.h │ ├── CodeBlocks.h │ ├── Make.h │ ├── Vcxproj.h │ ├── Vcproj.h │ ├── Generator.cpp │ ├── JsonDb.cpp │ ├── Main.cpp │ ├── NetBeans.cpp │ └── CodeBlocks.cpp ├── generate ├── generate.bat ├── .gitignore ├── NOTICE ├── Marefile ├── compile ├── compile.bat └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | compile -crlf 2 | generate -crlf -------------------------------------------------------------------------------- /example/pch/mypch.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | -------------------------------------------------------------------------------- /example/pch/mypch.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "mypch.h" 3 | -------------------------------------------------------------------------------- /src/libmare/Tools/String.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/craflin/mare/master/src/libmare/Tools/String.cpp -------------------------------------------------------------------------------- /generate: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [ ! -f "build/Debug/mare/mare" ] && ./compile 4 | [ ! -z "$1" ] && build/Debug/mare/mare $@ 5 | 6 | -------------------------------------------------------------------------------- /example/pch/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "mypch.h" 3 | 4 | int main() 5 | { 6 | std::cout << "hello world!\n"; 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /example/helloworld/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int main() 5 | { 6 | std::cout << "hello world!\n"; 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /generate.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if not exist build\Debug\mare\mare.exe call compile.bat 4 | if not "%1"=="" build\Debug\mare\mare.exe %* 5 | -------------------------------------------------------------------------------- /src/libmare/Tools/Error.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "String.h" 5 | 6 | class Error 7 | { 8 | public: 9 | static String getString(); 10 | }; 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | 3 | *.filters 4 | *.sln 5 | *.suo 6 | *.user 7 | *.vcproj 8 | *.vcxproj 9 | *.*sdf 10 | 11 | .* 12 | Makefile 13 | *.mk 14 | *.project 15 | *.workspace 16 | *.txt 17 | 18 | -------------------------------------------------------------------------------- /src/libmare/Tools/Assert.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #ifndef NDEBUG 5 | #include 6 | #define ASSERT(exp) assert(exp) 7 | #define VERIFY(exp) assert(exp) 8 | #else 9 | #define ASSERT(exp) 10 | #define VERIFY(exp) exp 11 | #endif 12 | -------------------------------------------------------------------------------- /example/helloworld/Marefile: -------------------------------------------------------------------------------- 1 | whom = "world" 2 | 3 | linkFlags += { 4 | if tool == "vcxproj" { "/SUBSYSTEM:CONSOLE" } 5 | } 6 | 7 | targets = { 8 | helloworld = cppApplication + { 9 | files = { 10 | "*.cpp" = cppSource 11 | } 12 | } 13 | command_test = { 14 | command = "echo Hello, $(whom)!" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/mare/Tools/md5.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | class MD5 5 | { 6 | public: 7 | MD5(); 8 | void update(const unsigned char *buf, unsigned len); 9 | void final(unsigned char digest[16]); 10 | 11 | private: 12 | unsigned int buf[4]; 13 | unsigned int bits[2]; 14 | unsigned char in[64]; 15 | 16 | void transform(unsigned int buf[4], unsigned int in[16]); 17 | }; 18 | -------------------------------------------------------------------------------- /src/mare/JsonDb.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/Map.h" 5 | #include "Tools/String.h" 6 | 7 | class Engine; 8 | class File; 9 | 10 | class JsonDb 11 | { 12 | public: 13 | 14 | JsonDb(Engine& engine) : engine(engine) {} 15 | 16 | bool generate(const Map& userArgs); 17 | 18 | private: 19 | 20 | static void writeJsonEscaped(File& f, const String& string); 21 | 22 | Engine& engine; 23 | }; 24 | -------------------------------------------------------------------------------- /example/pch/Marefile: -------------------------------------------------------------------------------- 1 | 2 | linkFlags += { 3 | if tool == "vcxproj" { "/SUBSYSTEM:CONSOLE" } 4 | } 5 | 6 | targets = { 7 | pch = cppApplication + { 8 | if tool == "vcxproj" { cppFlags += "/Yu\"mypch.h\"" } 9 | files = { 10 | "*.cpp" = cppSource 11 | "*.h" 12 | if tool == "vcxproj" { 13 | "mypch.cpp" = cppSource + { 14 | cppFlags += "/Yc" 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/libmare/Tools/Scope.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | class Scope 5 | { 6 | public: 7 | class Object 8 | { 9 | public: 10 | Scope& scope; 11 | 12 | public: 13 | Object(Scope& scope); 14 | 15 | virtual ~Object(); 16 | 17 | private: 18 | Object* next; 19 | Object* previous; 20 | }; 21 | 22 | Scope() : first(0) {} 23 | 24 | virtual ~Scope(); 25 | 26 | Scope& operator=(const Scope& other); 27 | 28 | private: 29 | Object* first; 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2011 Colin Graf 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /src/libmare/Tools/Error.cpp: -------------------------------------------------------------------------------- 1 | 2 | #ifdef _WIN32 3 | #include 4 | #else 5 | #include 6 | #include 7 | #endif 8 | 9 | #include "Error.h" 10 | 11 | String Error::getString() 12 | { 13 | #ifdef _WIN32 14 | String result; 15 | DWORD len = FormatMessageA( 16 | FORMAT_MESSAGE_FROM_SYSTEM | 17 | FORMAT_MESSAGE_IGNORE_INSERTS, 18 | NULL, 19 | GetLastError(), 20 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 21 | (LPTSTR) result.getData(256), 22 | 256, NULL ); 23 | if(len > 0) 24 | result.setLength(len); 25 | return result; 26 | #else 27 | return String(strerror(errno), -1); 28 | #endif 29 | } 30 | -------------------------------------------------------------------------------- /src/libmare/Tools/Scope.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Assert.h" 3 | #include "Scope.h" 4 | 5 | Scope::Object::Object(Scope& scope) : scope(scope), previous(0) 6 | { 7 | if((next = scope.first)) 8 | next->previous = this; 9 | scope.first = this; 10 | } 11 | 12 | Scope::Object::~Object() 13 | { 14 | if(next) 15 | next->previous = previous; 16 | if(previous) 17 | previous->next = next; 18 | else 19 | scope.first = next; 20 | } 21 | 22 | Scope::~Scope() 23 | { 24 | while(first) 25 | delete first; 26 | } 27 | 28 | Scope& Scope::operator=(const Scope& other) 29 | { 30 | while(first) 31 | delete first; 32 | ASSERT(other.first == 0); 33 | return *this; 34 | } 35 | -------------------------------------------------------------------------------- /src/mare/Tools/Win32/getopt.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | extern char *optarg; /* pointer to current option argument */ 9 | extern int optind; /* index for first non-option arg */ 10 | extern int opterr; /* enable built-in error messages */ 11 | extern int optopt; /* return value for option being evaluated */ 12 | 13 | struct option 14 | { 15 | const char* name; 16 | int has_arg; 17 | int* flag; 18 | int val; 19 | }; 20 | 21 | #define no_argument 0 22 | #define required_argument 1 23 | #define optional_argument 2 24 | 25 | int getopt(int argc, char* const argv[], const char* optstring); 26 | int getopt_long(int argc, char* const argv[], const char* optstring, const struct option* opts, int* index); 27 | int getopt_long_only(int argc, char* const argv[], const char* optstring, const struct option* opts, int* index); 28 | 29 | #ifdef __cplusplus 30 | }; 31 | #endif 32 | -------------------------------------------------------------------------------- /src/libmare/Token.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/String.h" 5 | 6 | class Token 7 | { 8 | public: 9 | enum Id 10 | { 11 | leftParenthesis, rightParenthesis, leftBrace, rightBrace, comma, 12 | assignment, plusAssignment, minusAssignment, 13 | plus, minus, not_, 14 | interrogation, colon, 15 | and_, or_, 16 | equal, notEqual, greaterThan, lowerThan, greaterEqualThan, lowerEqualThan, 17 | string, quotedString, 18 | eof, 19 | numOfTokens, 20 | }; 21 | Id id; 22 | String value; 23 | 24 | const char* getName(Id id) const 25 | { 26 | static const char* names[] = { 27 | "(", ")", "{", "}", ",", 28 | "=", "+=", "-=", 29 | "+", "-", "!", 30 | "?", ":", 31 | "&&", "||", 32 | "==", "!=", ">", "<", ">=", "<=", 33 | "string", "string", 34 | "end of file" 35 | }; 36 | return names[id]; 37 | } 38 | const char* getName() const {return getName(id);} 39 | }; 40 | -------------------------------------------------------------------------------- /Marefile: -------------------------------------------------------------------------------- 1 | 2 | if (tool == "vcxproj") { 3 | platforms = { "x64", "Win32" } 4 | } 5 | 6 | linkFlags += { 7 | if tool == "vcxproj" { "/SUBSYSTEM:CONSOLE" } 8 | } 9 | 10 | cppFlags += { 11 | if tool == "vcxproj" && configuration == "Release" { -"/O2", "/O1 /MT /GF" } 12 | } 13 | 14 | buildDir = "build/$(platform)/$(configuration)/$(target)" 15 | 16 | targets = { 17 | mare = cppApplication + { 18 | dependencies = { 19 | "libmare" 20 | } 21 | libs = { 22 | "mare" 23 | } 24 | libPaths = { 25 | "$(dir $(buildDir))/libmare" 26 | } 27 | includePaths = { 28 | "src/libmare" 29 | } 30 | root = "src/mare" 31 | files = { 32 | "src/mare/**.cpp" = cppSource 33 | "src/mare/**.h" 34 | } 35 | if (platform != "Win32" && platform != "x64") { 36 | files -= "src/mare/Tools/Win32/**" 37 | } 38 | } 39 | libmare = cppStaticLibrary + { 40 | root = "src/libmare" 41 | files = { 42 | "src/libmare/**.cpp" = cppSource 43 | "src/libmare/**.h" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/libmare/Tools/File.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "String.h" 5 | 6 | class File 7 | { 8 | public: 9 | enum Flags 10 | { 11 | readFlag = 0x0001, 12 | writeFlag = 0x0002, 13 | }; 14 | 15 | File(); 16 | ~File(); 17 | 18 | bool open(const String& file, Flags flags = readFlag); 19 | void close(); 20 | size_t read(char* buffer, size_t len); 21 | size_t write(const char* buffer, size_t len); 22 | bool write(const String& data); 23 | 24 | static String getDirname(const String& file); 25 | static String getBasename(const String& file); 26 | static String getExtension(const String& file); 27 | static String getWithoutExtension(const String& file); 28 | static String simplifyPath(const String& path); 29 | static String relativePath(const String& from, const String& to); 30 | static bool isPathAbsolute(const String& path); 31 | 32 | static bool getWriteTime(const String& file, long long& writeTime); 33 | 34 | static bool exists(const String& file); 35 | static bool unlink(const String& file); 36 | 37 | private: 38 | void* fp; 39 | }; 40 | -------------------------------------------------------------------------------- /src/libmare/Tools/Word.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "String.h" 5 | #include "List.h" 6 | 7 | class Word : public String 8 | { 9 | public: 10 | enum Flags 11 | { 12 | quotedFlag = (1 << 1), 13 | 14 | defaultFlag = (1 << 16), /**< used for built-in keys */ 15 | commandLineFlag = (1 << 17), /**< used for command line keys */ 16 | }; 17 | 18 | unsigned int flags; 19 | 20 | Word(const String& word, unsigned int flags) : String(word), flags(flags) {} 21 | 22 | Word& operator=(const Word& other); 23 | Word& operator=(const String& other); 24 | 25 | bool operator==(const Word& other) const; 26 | bool operator!=(const Word& other) const; 27 | 28 | void appendTo(String& text) const; 29 | 30 | /** 31 | * Returns the first word within string 32 | * @param text The string 33 | * @return The first word 34 | */ 35 | static String first(const String& text); 36 | 37 | static void split(const String& text, List& words); 38 | static void append(const List& words, String& text); 39 | static void splitLines(const String& text, List& words); 40 | }; 41 | -------------------------------------------------------------------------------- /src/mare/Mare.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/List.h" 5 | #include "Tools/Map.h" 6 | 7 | class Engine; 8 | class Word; 9 | class String; 10 | 11 | class Mare 12 | { 13 | public: 14 | 15 | Mare(Engine& engine, List& inputPlatforms, List& inputConfigs, List& inputTargets, bool showDebug, bool clean, bool rebuild, int jobs, bool ignoreDependencies) : 16 | engine(engine), showDebug(showDebug), clean(clean), rebuild(rebuild), jobs(jobs), ignoreDependencies(ignoreDependencies), inputPlatforms(inputPlatforms), inputConfigs(inputConfigs), inputTargets(inputTargets) {} 17 | 18 | bool build(const Map& userArgs); 19 | 20 | static String join(const List& words); 21 | 22 | private: 23 | Engine& engine; 24 | bool showDebug; 25 | bool clean; 26 | bool rebuild; 27 | int jobs; 28 | bool ignoreDependencies; 29 | 30 | List& inputPlatforms; 31 | List& inputConfigs; 32 | List& inputTargets; 33 | List allTargets; 34 | 35 | bool buildFile(); 36 | bool buildTargets(const String& platform, const String& configuration); 37 | 38 | friend class Rule; 39 | }; 40 | -------------------------------------------------------------------------------- /src/libmare/Tools/Process.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #pragma once 4 | 5 | #include "String.h" 6 | #include "Map.h" 7 | 8 | class Process 9 | { 10 | public: 11 | 12 | Process(); 13 | ~Process(); 14 | 15 | /** 16 | * Starts the execution of a process 17 | * @param command The command used to start the process. The first word in \c command should be a path to the executable. All other words in \c command are used as arguments for launching the process. 18 | * @return The process id of the newly started process or \c 0 if an errors occured 19 | */ 20 | unsigned int start(const String& command); 21 | 22 | /** 23 | * Returns the running state of the process 24 | * @return \c true when the process is currently running and can be joined using \c join() 25 | */ 26 | bool isRunning() const; 27 | 28 | unsigned int join(); 29 | 30 | static unsigned int waitOne(); 31 | 32 | static unsigned int getProcessorCount(); 33 | 34 | static String getArchitecture(); 35 | 36 | /** 37 | * Returns the environment variables of the current process. 38 | * @return A map that contains the environment variables. 39 | */ 40 | static const Map& getEnvironmentVariables(); 41 | 42 | private: 43 | #ifdef _WIN32 44 | void* hProcess; 45 | #else 46 | unsigned int pid; 47 | unsigned int exitCode; 48 | #endif 49 | }; 50 | -------------------------------------------------------------------------------- /src/mare/CMake.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Generator.h" 5 | 6 | class CMake : public Generator 7 | { 8 | public: 9 | CMake(Engine& engine) : Generator(engine) {} 10 | 11 | private: 12 | class Library 13 | { 14 | public: 15 | enum Type 16 | { 17 | localType, 18 | externalType, 19 | }; 20 | 21 | public: 22 | String name; 23 | Type type; 24 | }; 25 | 26 | class ProjectConfiguration 27 | { 28 | public: 29 | enum Type 30 | { 31 | applicationType, 32 | dynamicLibraryType, 33 | staticLibraryType, 34 | customTargetType, 35 | }; 36 | 37 | public: 38 | const Target* target; 39 | Type type; 40 | List sourceFiles; 41 | List customBuildFiles; 42 | List libs; 43 | }; 44 | 45 | class Project 46 | { 47 | public: 48 | Map configurations; 49 | }; 50 | 51 | private: 52 | bool writeWorkspace(); 53 | bool writeProjects(); 54 | bool writeProject(const String& targetName, Project& project); 55 | 56 | private: 57 | static String join(const List& items); 58 | static String joinPaths(const List& items, bool absolute = false); 59 | static String translatePath(const String& path, bool absolute = true); 60 | 61 | private: // Generator 62 | virtual void addDefaultKeys(Engine& engine); 63 | virtual bool processData(const Data& data); 64 | virtual bool writeFiles(); 65 | 66 | private: 67 | String workspaceName; 68 | List configurations; 69 | Map projects; 70 | }; 71 | -------------------------------------------------------------------------------- /src/mare/CodeLite.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Generator.h" 5 | 6 | class CodeLite : public Generator 7 | { 8 | public: 9 | CodeLite(Engine& engine) : Generator(engine) {} 10 | 11 | private: 12 | class ProjectFile; 13 | 14 | class FileTreeNode 15 | { 16 | public: 17 | Map folders; 18 | Map files; 19 | 20 | ~FileTreeNode() 21 | { 22 | for(Map::Node* i = folders.getFirst(); i; i = i->getNext()) 23 | delete i->data; 24 | } 25 | }; 26 | 27 | class ProjectConfiguration 28 | { 29 | public: 30 | const Target* target; 31 | }; 32 | 33 | class ProjectFile 34 | { 35 | public: 36 | class Configuration 37 | { 38 | public: 39 | const File* file; 40 | }; 41 | 42 | Map configurations; 43 | String folder; 44 | }; 45 | 46 | class Project 47 | { 48 | public: 49 | Map configurations; 50 | Map files; 51 | Map roots; 52 | FileTreeNode fileTree; 53 | }; 54 | 55 | String workspaceName; 56 | List configurations; 57 | Map projects; 58 | 59 | 60 | virtual void addDefaultKeys(Engine& engine); 61 | virtual bool processData(const Data& data); 62 | virtual bool writeFiles(); 63 | 64 | bool writeWorkspace(); 65 | bool writeProject(const String& projectName, const Project& project); 66 | 67 | static String join(const List& items, char sep = ';', const String& suffix = String()); 68 | static String joinCommands(const List& commands); 69 | static String xmlEscape(const String& text); 70 | }; 71 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | MARE_BUILD_DIR="build/Debug/mare" 5 | MARE_OUTPUT_DIR="build/Debug/mare" 6 | MARE_SOURCE_DIR="src" 7 | MARE_SOURCE_FILES="mare/Generator.cpp mare/CMake.cpp mare/CodeBlocks.cpp mare/CodeLite.cpp mare/JsonDb.cpp mare/Main.cpp mare/Make.cpp mare/Mare.cpp mare/NetBeans.cpp mare/Vcproj.cpp mare/Vcxproj.cpp mare/Tools/md5.cpp libmare/Engine.cpp libmare/Namespace.cpp libmare/Parser.cpp libmare/Statement.cpp libmare/Tools/Directory.cpp libmare/Tools/Error.cpp libmare/Tools/File.cpp libmare/Tools/Process.cpp libmare/Tools/Scope.cpp libmare/Tools/String.cpp libmare/Tools/Word.cpp" 8 | 9 | 10 | [ -z "$CXX" ] && CXX=g++ 11 | 12 | 13 | getArgs() 14 | { 15 | while [ ! -z "$1" ]; do 16 | case $1 in 17 | --buildDir=*) 18 | MARE_BUILD_DIR="${1#--buildDir=}" 19 | ;; 20 | --outputDir=*) 21 | MARE_OUTPUT_DIR="${1#--outputDir=}" 22 | ;; 23 | --sourceDir=*) 24 | MARE_SOURCE_DIR="${1#--sourceDir=}" 25 | ;; 26 | *) 27 | echo "Usage: $0 [--buildDir=] [--outputDir=] [--sourceDir=]" 28 | exit 1 29 | ;; 30 | esac 31 | shift 32 | done 33 | } 34 | 35 | build() 36 | { 37 | if [ ! -f "$MARE_SOURCE_DIR/mare/Main.cpp" ]; then 38 | echo "error: Could not find source files." 39 | exit 1 40 | fi 41 | mkdir -p "$MARE_BUILD_DIR" 42 | mkdir -p "$MARE_OUTPUT_DIR" 43 | rm -f "$MARE_OUTPUT_DIR/mare" 44 | MARE_OBJECTS= 45 | for file in $MARE_SOURCE_FILES; do 46 | OBJECT="$MARE_BUILD_DIR/$(basename ${file%.cpp}).o" 47 | MARE_OBJECTS="$MARE_OBJECTS $OBJECT" 48 | echo "$MARE_SOURCE_DIR/$file" 49 | $CXX -Wall -g -I"$MARE_SOURCE_DIR/libmare" -o "$OBJECT" -c "$MARE_SOURCE_DIR/$file" 50 | done 51 | echo "-> $MARE_OUTPUT_DIR/mare" 52 | $CXX -o "$MARE_OUTPUT_DIR/mare" $MARE_OBJECTS 53 | } 54 | 55 | 56 | getArgs $@ || exit 1 57 | build || exit 1 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/mare/NetBeans.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/Map.h" 5 | #include "Tools/List.h" 6 | #include "Tools/String.h" 7 | #include "Tools/File.h" 8 | 9 | class Engine; 10 | 11 | class NetBeans 12 | { 13 | public: 14 | 15 | NetBeans(Engine& engine) : engine(engine) {} 16 | 17 | bool generate(const Map& userArgs); 18 | 19 | private: 20 | 21 | class Project 22 | { 23 | public: 24 | class Config 25 | { 26 | public: 27 | String name; /**< The name of the configuration without the platform extension */ 28 | 29 | List buildCommand; /**< For CustomBuild projects */ 30 | List reBuildCommand; /**< For CustomBuild projects */ 31 | List cleanCommand; /**< For CustomBuild projects */ 32 | String buildDir; 33 | String firstOutput; 34 | List defines; 35 | List includePaths; 36 | Config(const String& name) : name(name) {} 37 | }; 38 | 39 | class File 40 | { 41 | public: 42 | String name; 43 | String folder; 44 | 45 | File(const String& name) : name(name) {} 46 | }; 47 | 48 | String name; 49 | Map configs; 50 | Map files; 51 | Map roots; 52 | Map dependencies; 53 | 54 | Project(const String& name) : name(name) {} 55 | }; 56 | 57 | Engine& engine; 58 | 59 | File file; 60 | 61 | Map configs; 62 | Map projects; 63 | 64 | String openedFile; /**< The file that is currently written */ 65 | 66 | bool readFile(); 67 | 68 | bool processData(); 69 | bool generateProject(Project& project); 70 | 71 | void fileOpen(const String& name); 72 | void fileWrite(const String& data); 73 | void fileClose(); 74 | 75 | //static String join(const List& items, char sep = ';', const String& suffix = String()); 76 | 77 | static String joinCommands(const List& commands); 78 | static String xmlEscape(const String& text); 79 | }; 80 | 81 | -------------------------------------------------------------------------------- /src/libmare/Tools/Directory.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Directory.h 3 | * Declaration of a class for accessing directories. 4 | * @author Colin Graf 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "String.h" 10 | #include "List.h" 11 | 12 | /** A Class for accessing directories */ 13 | class Directory 14 | { 15 | public: 16 | 17 | /** Default constructor */ 18 | Directory(); 19 | 20 | /** Destructor */ 21 | ~Directory(); 22 | 23 | /** 24 | * Opens a directory for searching for files in the directory 25 | * @param dirpath The path to the directory to search in 26 | * @param pattern A search pattern like "*.inf" 27 | * @param dirsOnly Search only for directories and ignore files 28 | * @return Whether the directory was opened successfully 29 | */ 30 | bool open(const String& dirpath, const String& pattern, bool dirsOnly); 31 | 32 | /** 33 | * Searches the next matching entry in the opened directory 34 | * @param path The path of the next matching entry 35 | * @param isDir Whether the next entry is a directory 36 | * @return Whether a matching entry was found 37 | */ 38 | bool read(String& path, bool& isDir); 39 | 40 | static void findFiles(const String& pattern, List& files); 41 | 42 | static bool exists(const String& dir); 43 | 44 | static bool create(const String& dir); 45 | 46 | static bool remove(const String& dir); 47 | 48 | static bool change(const String& dir); 49 | 50 | static String getCurrent(); 51 | 52 | private: 53 | bool dirsOnly; 54 | #ifdef _WIN32 55 | void* findFile; /**< Win32 FindFirstFile HANDLE */ 56 | char ffd[320]; /**< Buffer for WIN32_FIND_DATA */ 57 | bool bufferedEntry; /**< Whether there is a buffered search result in ffd. */ 58 | String dirpath; /**< The name of the directory. */ 59 | String patternExtension; /**< A search pattern file name extension (e.g. "inf" of "*.inf") */ 60 | #else 61 | void* dp; /**< Directory descriptor. */ 62 | String dirpath; /**< The path to the directory to search in */ 63 | String pattern; /**< A search pattern like "*.inf" */ 64 | #endif 65 | }; 66 | -------------------------------------------------------------------------------- /src/libmare/Tools/Array.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | template class Array 5 | { 6 | public: 7 | Array() : data(0), size(0), capacity(0) {} 8 | 9 | ~Array() 10 | { 11 | if(data) 12 | delete[] data; 13 | } 14 | 15 | T* getFirst() {return data;} 16 | const T* getFirst() const {return data;} 17 | 18 | const T* getData() const {return data;} 19 | T* getData(size_t size) const 20 | { 21 | grow(size, 0); 22 | return data; 23 | } 24 | 25 | T& append(const T& elem = T()) 26 | { 27 | size_t newSize = size + 1; 28 | grow(newSize, size); 29 | T& result = data[size]; 30 | result = elem; 31 | size = newSize; 32 | return result; 33 | } 34 | 35 | void remove(size_t index) 36 | { 37 | if(index < size) 38 | { 39 | --size; 40 | for(T* pos = data + index, * end = data + size; pos < end; ++pos) 41 | *pos = pos[1]; 42 | } 43 | } 44 | 45 | ptrdiff_t find(const T& elem) 46 | { 47 | for(T* pos = data, * end = data + size; pos < end; ++pos) 48 | if(*pos == elem) 49 | return pos - data; 50 | return -1; 51 | } 52 | 53 | inline size_t getSize() const {return size;} 54 | void setSize(size_t size) 55 | { 56 | grow(size, size); 57 | this->size = size; 58 | } 59 | 60 | inline size_t getCapacity() const {return capacity;} 61 | inline void setCapacity(size_t capacity) {grow(capacity, size);} 62 | 63 | inline void clear() {size = 0;} 64 | inline bool isEmpty() const {return size == 0;} 65 | 66 | private: 67 | T* data; 68 | size_t size; 69 | size_t capacity; 70 | 71 | void grow(size_t capacity, size_t size) 72 | { 73 | if(this->capacity < capacity) 74 | { 75 | T* oldData = data; 76 | this->capacity = capacity + 16 + (capacity >> 1); 77 | data = new T[this->capacity]; 78 | for(T* pos = data, * end = data + this->size, * oldPos = oldData; pos < end;) 79 | *(pos++) = *(oldPos++); 80 | if(oldData) 81 | delete[] oldData; 82 | this->size = size; 83 | } 84 | } 85 | }; 86 | 87 | -------------------------------------------------------------------------------- /src/libmare/Engine.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/String.h" 5 | #include "Tools/List.h" 6 | #include "Tools/Map.h" 7 | #include "Tools/Scope.h" 8 | 9 | class Namespace; 10 | class Word; 11 | class Statement; 12 | 13 | class Engine : public Scope 14 | { 15 | public: 16 | 17 | typedef void (*ErrorHandler)(void* userData, const String& file, int line, const String& message); 18 | 19 | Engine(ErrorHandler errorHandler, void* userData) : errorHandler(errorHandler), errorUserData(userData), rootStatement(0), currentSpace(0) {} 20 | 21 | bool load(const String& file); 22 | void error(const String& message); 23 | 24 | bool hasKey(const String& key, bool allowInheritance = true); 25 | 26 | bool enterKey(const String& key, bool allowInheritance = true); 27 | void enterUnnamedKey(); 28 | void enterNewKey(const String& key); 29 | void enterRootKey(); 30 | 31 | String getKeyOrigin(const String& key); 32 | 33 | bool leaveKey(); 34 | 35 | void getKeys(List& keys); 36 | bool getKeys(const String& key, List& keys, bool allowInheritance = true); 37 | String getFirstKey(); 38 | String getFirstKey(const String& key, bool allowInheritance = true); 39 | void getText(List& text); 40 | bool getText(const String& key, List& text, bool allowInheritance = true); 41 | String getMareDir() const; 42 | 43 | void addDefaultKey(const String& key); 44 | void addDefaultKey(const String& key, const String& value); 45 | void addDefaultKey(const String& key, const Map& value); 46 | void addCommandLineKey(const String& key, const String& value); 47 | 48 | void pushAndLeaveKey(); // TODO: hide these functions 49 | bool popKey(); 50 | 51 | private: 52 | ErrorHandler errorHandler; 53 | void* errorUserData; 54 | Statement* rootStatement; 55 | Namespace* currentSpace; 56 | List stashedKeys; 57 | 58 | bool resolveScript(const String& key, Word*& word, Namespace*& result); 59 | bool resolveScript(const String& key, Namespace* excludeStatements, Word*& word, Namespace*& result); 60 | void setKey(const Word& key); 61 | void appendKeys(String& output); 62 | 63 | friend class ReferenceStatement; // hack? 64 | friend class IfStatement; // hack? 65 | friend class Namespace; 66 | }; 67 | -------------------------------------------------------------------------------- /src/libmare/Parser.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #pragma once 4 | 5 | #include "Tools/File.h" 6 | #include "Tools/String.h" 7 | #include "Engine.h" 8 | #include "Token.h" 9 | 10 | class Statement; 11 | 12 | class Parser 13 | { 14 | public: 15 | 16 | Parser(Engine& engine); 17 | 18 | Statement* parse(const String& file, Engine::ErrorHandler errorHandler, void* userData); 19 | 20 | public: 21 | class IncludeFile : public Scope, Scope::Object 22 | { 23 | public: 24 | IncludeFile(Scope& scope) : Scope::Object(scope) {} 25 | 26 | String fileDir; 27 | }; 28 | 29 | private: 30 | Engine& engine; 31 | Engine::ErrorHandler errorHandler; 32 | void* errorHandlerUserData; 33 | IncludeFile* includeFile; 34 | String filePath; 35 | File file; 36 | 37 | char readBuffer[2024]; 38 | const char* readBufferPos; 39 | const char* readBufferEnd; 40 | 41 | unsigned int currentLine; /**< Starting with 0 */ 42 | char currentChar; 43 | Token currentToken; 44 | 45 | void nextChar(); 46 | void nextToken(); 47 | 48 | void unexpectedChar(char c); 49 | void expectToken(Token::Id id); 50 | 51 | void readString(String& string); 52 | 53 | /** file = { statement } EOF */ 54 | Statement* readFile(); 55 | 56 | /** statements ::= '{' { statement } '}' | statement */ 57 | Statement* readStatements(); 58 | 59 | /** statement ::= ( 'if' expression statements [ 'else' statements ] | 'include' ( string | quotedstring ) | assignment ) { ( ',' | ';' ) } */ 60 | Statement* readStatement(); 61 | 62 | /** assignment ::= '-' ( string | quotedstring ) | ( string | quotedstring ) [ ( '=' | '+=' | '-=' ) expression */ 63 | Statement* readAssignment(); 64 | 65 | /** expression ::= orformula [ '?' expression ':' expression ] */ 66 | Statement* readExpression(); 67 | 68 | /** orformula ::= andformula { '||' andformula } */ 69 | Statement* readOrForumla(); 70 | 71 | /** andformula ::= comparison { '&&' comparison } */ 72 | Statement* readAndFormula(); 73 | 74 | /** comparison ::= relation { ( '==' | '!=' ) relation } */ 75 | Statement* readComparison(); 76 | 77 | /** relation ::= concatination { ( '<' | '>' | '<=' | '>=' ) concatination } */ 78 | Statement* readRelation(); 79 | 80 | /** concatination ::= value { ( '+' | '-' ) value } */ 81 | Statement* readConcatination(); 82 | 83 | /** value ::= '!' value | '(' expression ')' | '{' { statement } '}' | 'true' | 'false' | string | quotedstring */ 84 | Statement* readValue(); 85 | }; 86 | -------------------------------------------------------------------------------- /src/mare/Generator.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/Map.h" 5 | #include "Tools/List.h" 6 | #include "Tools/String.h" 7 | #include "Tools/File.h" 8 | 9 | class Engine; 10 | 11 | class Generator 12 | { 13 | public: 14 | Generator(Engine& engine) : engine(engine) {} 15 | 16 | bool generate(const Map& userArgs); 17 | 18 | protected: 19 | class File 20 | { 21 | public: 22 | String folder; 23 | List command; 24 | List message; 25 | List output; 26 | List input; 27 | List dependencies; 28 | bool hasCppFlags; 29 | List cppFlags; 30 | bool hasCFlags; 31 | List cFlags; 32 | }; 33 | 34 | class Target 35 | { 36 | public: 37 | String folder; 38 | List buildCommand; 39 | List reBuildCommand; 40 | List cleanCommand; 41 | List preBuildCommand; 42 | List preLinkCommand; 43 | List postBuildCommand; 44 | String buildDir; 45 | List command; 46 | List message; 47 | List output; 48 | List input; 49 | List dependencies; 50 | List cppFlags; 51 | List cFlags; 52 | List linkFlags; 53 | List defines; 54 | List includePaths; 55 | List libPaths; 56 | List libs; 57 | List root; 58 | List cppCompiler; 59 | List cCompiler; 60 | Map files; 61 | }; 62 | 63 | class Configuration 64 | { 65 | public: 66 | Map targets; 67 | }; 68 | 69 | class Platform 70 | { 71 | public: 72 | Map configurations; 73 | }; 74 | 75 | class Data 76 | { 77 | public: 78 | String name; /**< Name of the workspace or solution. */ 79 | Map platforms; 80 | }; 81 | 82 | virtual void addDefaultKeys(Engine& engine) = 0; 83 | virtual bool processData(const Data& data) = 0; 84 | virtual bool writeFiles() = 0; 85 | 86 | void fileOpen(const String& name); 87 | void fileWrite(const String& data); 88 | void fileClose(); 89 | 90 | void error(const String& message); 91 | 92 | private: 93 | Engine& engine; 94 | 95 | ::File file; 96 | String openedFile; /**< The file that is currently written */ 97 | 98 | bool readFile(Data& data); 99 | }; 100 | -------------------------------------------------------------------------------- /src/libmare/Namespace.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/Map.h" 5 | #include "Tools/Scope.h" 6 | #include "Tools/Word.h" 7 | #include "Token.h" 8 | 9 | class Engine; 10 | class Statement; 11 | 12 | class Namespace : public Scope, public Scope::Object 13 | { 14 | public: 15 | Namespace(Scope& scope, Namespace* parent, Engine* engine, Statement* statement, Namespace* next, unsigned int flags) : Scope::Object(scope), parent(parent), defaultStatement(0), statement(statement), next(next), engine(engine), flags(flags) {} 16 | 17 | inline Namespace* getParent() {return parent;} 18 | bool resolveScript2(const String& name, Word*& word, Namespace*& result); 19 | bool resolveScript2(const String& name, Namespace* excludeStatements, Word*& word, Namespace*& result); 20 | Namespace* enterKey(const String& name, bool allowInheritance); 21 | Namespace* enterUnnamedKey(Statement* statement); 22 | Namespace* enterNewKey(const String& name); 23 | String getKeyOrigin(const String& key); 24 | void getKeys(List& keys); 25 | void getText(List& text); 26 | void appendKeys(String& output); 27 | String getFirstKey(); 28 | String getMareDir() const; 29 | inline Engine& getEngine() {return *engine;} 30 | 31 | void addKey(const String& key, unsigned int flags, Statement* value, Token::Id operation = Token::assignment); 32 | void addKeyRaw(const Word& key, Statement* value, Token::Id operation = Token::assignment); 33 | void setKeyRaw(const Word& key); 34 | void removeAllKeys(); 35 | void removeKey(const String& key); 36 | void removeKeyRaw(const String& key); 37 | void removeKeysRaw(Namespace& space); 38 | bool compareKeys(Namespace& space, bool& result); 39 | bool versionCompareKeys(Namespace& space, int& result); 40 | 41 | void addDefaultStatement(Statement* statement); 42 | void addDefaultKey(const String& key); 43 | void addDefaultKey(const String& key, unsigned int flags, const String& value); 44 | void addDefaultKey(const String& key, unsigned int flags, const Map& value); 45 | 46 | private: 47 | enum Flags 48 | { 49 | inheritedFlag = (1 << 1), 50 | compiledFlag = (1 << 2), 51 | compilingFlag = (1 << 3), 52 | unnamedFlag = (1 << 4), 53 | textModeFlag = (1 << 5), 54 | }; 55 | 56 | Namespace* parent; 57 | Statement* defaultStatement; 58 | Statement* statement; 59 | Namespace* next; 60 | Engine* engine; 61 | unsigned int flags; 62 | Map variables; 63 | 64 | void compile(); 65 | String evaluateString(const String& string) const; 66 | 67 | friend class Engine; 68 | friend class ReferenceStatement; // temporary hack 69 | }; 70 | -------------------------------------------------------------------------------- /src/libmare/Tools/String.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file String.h 3 | * Delcaration of a lightweight not multithread safe reference counting lazy copying string 4 | * @author Colin Graf 5 | */ 6 | 7 | #pragma once 8 | 9 | #include // for ptrdiff_t and size_t on Linux 10 | 11 | class String 12 | { 13 | public: 14 | 15 | String() : data(&emptyData) {++data->refs;} 16 | 17 | String(const String& other) : data(other.data) {++data->refs;} 18 | 19 | template String(const char (&str)[N]) {init(N - 1, str, N - 1);} 20 | 21 | String(const char* str, ptrdiff_t length); 22 | 23 | String(size_t capacity) {init(capacity, 0, 0);} 24 | 25 | ~String(); 26 | 27 | String& operator=(const String& other); 28 | inline String& operator+=(const String& other) {return append(other);} 29 | inline String operator+(const String& other) const {return String(*this).append(other);} 30 | 31 | bool operator==(const String& other) const; 32 | bool operator!=(const String& other) const; 33 | 34 | inline const char* getData() const {return data->str;} 35 | 36 | char* getData(size_t capacity); 37 | 38 | void setCapacity(size_t capacity); 39 | 40 | void setLength(size_t length); 41 | inline size_t getLength() const {return data->length;} 42 | 43 | String& format(size_t capacity, const char* format, ...); 44 | 45 | String& prepend(const String& str); 46 | 47 | String& append(char c); 48 | String& append(const String& str); 49 | String& append(const char* str, size_t length); 50 | 51 | void clear(); 52 | bool isEmpty() const {return data->length == 0;} 53 | 54 | String substr(ptrdiff_t start, ptrdiff_t length = -1) const; 55 | 56 | bool patmatch(const String& pattern) const; 57 | bool patsubst(const String& pattern, const String& replace); 58 | 59 | int subst(const String& from, const String& to); 60 | 61 | /** 62 | * Find str in this String. "" is always contained. 63 | * 64 | * @param str The string to find. 65 | * @param pos The position of str in this String if contained, otherwise undefined. 66 | * @return Returns whether str was found. 67 | */ 68 | bool find(const String& str, size_t& pos) const; 69 | bool contains(const String& str) const; 70 | bool find(char ch, size_t& pos) const; 71 | 72 | String& lowercase(); 73 | String& uppercase(); 74 | 75 | private: 76 | class Data 77 | { 78 | public: 79 | const char* str; 80 | size_t length; // size TODO: rename 81 | size_t capacity; 82 | unsigned int refs; 83 | Data* next; 84 | 85 | Data() {} 86 | 87 | template Data(const char (&str)[N]) : str(str), length(N - 1), capacity(0), refs(1) {} 88 | }; 89 | 90 | Data* data; 91 | 92 | static Data emptyData; 93 | static Data* firstFreeData; 94 | 95 | void init(size_t capacity, const char* str, size_t length); 96 | void free(); 97 | void grow(size_t capacity, size_t length); 98 | }; 99 | -------------------------------------------------------------------------------- /src/libmare/Statement.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/String.h" 5 | #include "Tools/List.h" 6 | #include "Tools/Scope.h" 7 | #include "Token.h" 8 | 9 | class Namespace; 10 | 11 | class Statement : public Scope::Object 12 | { 13 | public: 14 | Statement(Scope& scope) : Scope::Object(scope) {} 15 | 16 | virtual void execute(Namespace& space) = 0; 17 | }; 18 | 19 | class BlockStatement : public Statement 20 | { 21 | public: 22 | List statements; 23 | 24 | BlockStatement(Scope& scope) : Statement(scope) {} 25 | 26 | private: 27 | virtual void execute(Namespace& space); 28 | }; 29 | 30 | class WrapperStatement : public Statement 31 | { 32 | public: 33 | Statement* statement; 34 | 35 | WrapperStatement(Scope& scope) : Statement(scope), statement(0) {} 36 | 37 | private: 38 | virtual void execute(Namespace& space); 39 | }; 40 | 41 | class AssignStatement : public Statement 42 | { 43 | public: 44 | AssignStatement(Scope& scope) : Statement(scope), operation(Token::assignment), flags(0), value(0) {} 45 | 46 | Token::Id operation; 47 | String variable; 48 | unsigned int flags; 49 | Statement* value; 50 | 51 | private: 52 | virtual void execute(Namespace& space); 53 | }; 54 | 55 | class RemoveStatement : public Statement 56 | { 57 | public: 58 | RemoveStatement(Scope& scope) : Statement(scope) {} 59 | 60 | String variable; 61 | 62 | private: 63 | virtual void execute(Namespace& space); 64 | }; 65 | 66 | class BinaryStatement : public Statement 67 | { 68 | public: 69 | BinaryStatement(Scope& scope) : Statement(scope) {} 70 | 71 | Token::Id operation; 72 | Statement* leftOperand; 73 | Statement* rightOperand; 74 | 75 | private: 76 | virtual void execute(Namespace& space); 77 | }; 78 | 79 | class UnaryStatement : public Statement 80 | { 81 | public: 82 | UnaryStatement(Scope& scope) : Statement(scope) {} 83 | 84 | Token::Id operation; 85 | Statement* operand; 86 | 87 | private: 88 | virtual void execute(Namespace& space); 89 | }; 90 | 91 | class StringStatement : public Statement 92 | { 93 | public: 94 | StringStatement(Scope& scope) : Statement(scope) {} 95 | 96 | String value; 97 | 98 | private: 99 | virtual void execute(Namespace& space); 100 | }; 101 | 102 | class ReferenceStatement : public Statement 103 | { 104 | public: 105 | ReferenceStatement(Scope& scope) : Statement(scope) {} 106 | 107 | String variable; 108 | 109 | private: 110 | virtual void execute(Namespace& space); 111 | }; 112 | 113 | /** "if ... then ... else ..." and " ... ? ... : ..." */ 114 | class IfStatement : public Statement 115 | { 116 | public: 117 | IfStatement(Scope& scope) : Statement(scope), elseStatements(0) {} 118 | 119 | Statement* condition; 120 | Statement* thenStatements; 121 | Statement* elseStatements; 122 | 123 | private: 124 | virtual void execute(Namespace& space); 125 | }; 126 | -------------------------------------------------------------------------------- /src/mare/CodeBlocks.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/Map.h" 5 | #include "Tools/List.h" 6 | #include "Tools/String.h" 7 | #include "Tools/File.h" 8 | 9 | class Engine; 10 | 11 | class CodeBlocks 12 | { 13 | public: 14 | 15 | CodeBlocks(Engine& engine) : engine(engine) {} 16 | 17 | bool generate(const Map& userArgs); 18 | 19 | private: 20 | 21 | class Project 22 | { 23 | public: 24 | class Config 25 | { 26 | public: 27 | String name; /**< The name of the configuration without the platform extension */ 28 | 29 | List buildCommand; /**< For CustomBuild projects */ 30 | List reBuildCommand; /**< For CustomBuild projects */ 31 | List cleanCommand; /**< For CustomBuild projects */ 32 | /* 33 | List preBuildCommand; 34 | List preLinkCommand; 35 | List postBuildCommand; 36 | */ 37 | String buildDir; 38 | String type; 39 | List command; 40 | //List message; 41 | String firstOutput; 42 | bool customBuild; 43 | /* 44 | List outputs; 45 | List inputs; 46 | List dependencies; 47 | List cppFlags; 48 | Map linkFlags; 49 | List defines; 50 | List includePaths; 51 | List libPaths; 52 | List libs; 53 | */ 54 | Config(const String& name) : name(name), customBuild(false) {} 55 | }; 56 | 57 | class File 58 | { 59 | public: 60 | String name; 61 | String folder; 62 | 63 | File(const String& name) : name(name) {} 64 | }; 65 | 66 | String name; 67 | Map configs; 68 | Map files; 69 | Map dependencies; 70 | Map roots; 71 | 72 | Project(const String& name) : name(name) {} 73 | }; 74 | /* 75 | class ProjectFilter 76 | { 77 | public: 78 | String guid; 79 | List projects; 80 | 81 | ProjectFilter(const String& guid) : guid(guid) {} 82 | }; 83 | */ 84 | Engine& engine; 85 | 86 | File file; 87 | 88 | String workspaceName; 89 | Map configs; 90 | Map projects; 91 | //Map projectFilters; 92 | 93 | String openedFile; /**< The file that is currently written */ 94 | 95 | bool readFile(); 96 | 97 | bool processData(); 98 | 99 | bool generateWorkspace(); 100 | bool generateProjects(); 101 | bool generateProject(Project& project); 102 | 103 | void fileOpen(const String& name); 104 | void fileWrite(const String& data); 105 | void fileClose(); 106 | 107 | static String join(const List& items, char sep = ';', const String& suffix = String()); 108 | static String joinCommands(const List& commands); 109 | static String xmlEscape(const String& text); 110 | }; 111 | 112 | -------------------------------------------------------------------------------- /src/mare/Make.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/Map.h" 5 | #include "Tools/List.h" 6 | #include "Tools/String.h" 7 | #include "Tools/File.h" 8 | 9 | class Engine; 10 | 11 | class Make 12 | { 13 | public: 14 | 15 | Make(Engine& engine) : engine(engine) {} 16 | 17 | bool generate(const Map& userArgs); 18 | 19 | private: 20 | 21 | class Platform 22 | { 23 | public: 24 | class Config 25 | { 26 | public: 27 | class Target 28 | { 29 | public: 30 | class File 31 | { 32 | public: 33 | 34 | String name; 35 | String type; 36 | List output; 37 | List input; 38 | List command; 39 | List message; 40 | List dependencies; 41 | 42 | File(const String& name) : name(name) {} 43 | }; 44 | 45 | Target(const String& name) : name(name) {} 46 | 47 | String name; 48 | String type; 49 | List files; 50 | List output; 51 | List input; 52 | List command; 53 | List message; 54 | List dependencies; 55 | 56 | String buildDir; 57 | List soFlags; 58 | List cppFlags; 59 | List cFlags; 60 | List linkFlags; 61 | List defines; 62 | List includePaths; 63 | List libPaths; 64 | List libs; 65 | 66 | List cppCompiler; 67 | List cCompiler; 68 | List linker; 69 | 70 | Map outputDirs; 71 | List objects; 72 | List nonObjects; 73 | }; 74 | 75 | Config(const String& name) : name(name) {} 76 | 77 | String name; 78 | Map targets; 79 | }; 80 | 81 | Platform(const String& name) : name(name) {} 82 | 83 | String name; 84 | List configs; 85 | }; 86 | 87 | Engine& engine; 88 | 89 | File file; 90 | String openedFile; /**< The file that is currently written */ 91 | 92 | List platforms; 93 | 94 | bool readFile(); 95 | 96 | bool processData(); 97 | 98 | bool generateMetaMakefile(); 99 | void generateMetaMakefilePlatform(Platform& platform); 100 | void generateMetaMakefileConfig(const Platform& platform, const Platform::Config& config); 101 | bool generateMakefile(const Platform& platform, const Platform::Config& config); 102 | bool generateTargetMakefile(const Platform& platform, const Platform::Config& config, const Platform::Config::Target& target); 103 | 104 | void fileOpen(const String& name); 105 | void fileWrite(const String& data); 106 | void fileClose(); 107 | 108 | static String join(const List& items, const String& preifx = String()); 109 | static String joinCommands(const String& prefix, const String& suffix, const List& commands); 110 | }; 111 | 112 | -------------------------------------------------------------------------------- /src/libmare/Tools/Map.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | template class Map 5 | { 6 | public: 7 | class Node 8 | { 9 | public: 10 | K key; 11 | T data; 12 | 13 | Node(const K& key, const T& data) : key(key), data(data) {} 14 | 15 | inline Node* getNext() {return next;} 16 | inline const Node* getNext() const {return next;} 17 | inline Node* getPrevious() {return previous;} 18 | inline const Node* getPrevious() const {return previous;} 19 | 20 | private: 21 | Node* next; 22 | Node* previous; 23 | 24 | friend class Map; 25 | }; 26 | 27 | Map() : first(0), last(0), size(0), firstFree(0) {} 28 | 29 | ~Map() 30 | { 31 | clear(); 32 | for(Node* node = firstFree, * next; node; node = next) 33 | { 34 | next = node->next; 35 | delete node; 36 | } 37 | } 38 | 39 | Map& operator=(const Map& other) 40 | { 41 | clear(); 42 | for(Node* i = other.first; i; i = i->next) 43 | append(i->key, i->data); 44 | return *this; 45 | } 46 | 47 | T& append(const K& key, const T& data = T()) 48 | { 49 | Node* node; 50 | if(firstFree) 51 | { 52 | node = firstFree; 53 | firstFree = firstFree->next; 54 | node->key = key; 55 | node->data = data; 56 | } 57 | else 58 | node = new Node(key, data); 59 | node->next = 0; 60 | if((node->previous = last)) 61 | last->next = node; 62 | else 63 | first = node; 64 | last = node; 65 | ++size; 66 | return node->data; 67 | } 68 | 69 | void remove(Node* node) 70 | { 71 | if(node->next) 72 | node->next->previous = node->previous; 73 | else 74 | last = node->previous; 75 | if(node->previous) 76 | node->previous->next = node->next; 77 | else 78 | first = node->next; 79 | --size; 80 | node->next = firstFree; 81 | firstFree = node; 82 | } 83 | 84 | void clear() 85 | { 86 | if(first) 87 | { 88 | last->next = firstFree; 89 | firstFree = first; 90 | first = last = 0; 91 | size = 0; 92 | } 93 | } 94 | 95 | Node* find(const K& key) 96 | { 97 | // TODO: use hash map 98 | for(Node* node = first; node; node = node->next) 99 | if(node->key == key) 100 | return node; 101 | return 0; 102 | } 103 | 104 | const Node* find(const K& key) const 105 | { 106 | // TODO: use hash map 107 | for(const Node* node = first; node; node = node->next) 108 | if(node->key == key) 109 | return node; 110 | return 0; 111 | } 112 | 113 | T lookup(const K& key) const 114 | { 115 | // TODO: use hash map 116 | for(Node* node = first; node; node = node->next) 117 | if(node->key == key) 118 | return node->data; 119 | return T(); 120 | } 121 | 122 | inline Node* getFirst() {return first;} 123 | inline const Node* getFirst() const {return first;} 124 | inline Node* getLast() {return last;} 125 | inline const Node* getLast() const {return last;} 126 | 127 | inline unsigned int getSize() const {return size;} 128 | 129 | inline bool isEmpty() const {return first == 0;} 130 | 131 | private: 132 | Node* first; 133 | Node* last; 134 | unsigned int size; 135 | Node* firstFree; 136 | }; 137 | 138 | -------------------------------------------------------------------------------- /src/libmare/Tools/Word.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "Word.h" 6 | 7 | Word& Word::operator=(const Word& other) 8 | { 9 | (String&)*this = other; 10 | flags = other.flags; 11 | return *this; 12 | } 13 | 14 | Word& Word::operator=(const String& other) 15 | { 16 | (String&)*this = other; 17 | return *this; 18 | } 19 | 20 | bool Word::operator==(const Word& other) const 21 | { 22 | return *(String*)this == other; 23 | } 24 | 25 | bool Word::operator!=(const Word& other) const 26 | { 27 | return *(String*)this != other; 28 | } 29 | 30 | String Word::first(const String& text) 31 | { 32 | List words; 33 | split(text, words); 34 | return words.isEmpty() ? String() : words.getFirst()->data; 35 | } 36 | 37 | void Word::split(const String& text, List& words) 38 | { 39 | const char* str = text.getData(); 40 | for(;;) 41 | { 42 | while(isspace(*(unsigned char*)str)) 43 | ++str; 44 | if(!*str) 45 | break; 46 | if(*str == '"') 47 | { 48 | ++str; 49 | const char* end = str; 50 | for(; *end; ++end) 51 | if(*end == '\\' && end[1] == '"') 52 | ++end; 53 | else if(*end == '"') 54 | break; 55 | if(end > str) // TODO: read escaped spaces as ordinary spaces? 56 | words.append(Word(text.substr(str - text.getData(), end - str), Word::quotedFlag)); 57 | str = end; 58 | if(*str) 59 | ++str; // skip closing '"' 60 | } 61 | else 62 | { 63 | const char* end = str; 64 | for(; *end; ++end) 65 | if(isspace(*(unsigned char*)end)) 66 | break; 67 | // TODO: read escaped spaces as ordinary spaces 68 | words.append(Word(text.substr(str - text.getData(), end - str), 0)); 69 | str = end; 70 | } 71 | } 72 | } 73 | 74 | void Word::append(const List& words, String& text) 75 | { 76 | if(words.isEmpty()) 77 | return; 78 | 79 | size_t totalLen = words.getSize() * 3; 80 | for(const List::Node* i = words.getFirst(); i; i = i->getNext()) 81 | totalLen += i->data.getLength(); 82 | text.setCapacity(totalLen + 16); 83 | 84 | const List::Node* i = words.getFirst(); 85 | //const List::Node* previousWord = i; 86 | i->data.appendTo(text); 87 | for(i = i->getNext(); i; i = i->getNext()) 88 | { 89 | text.append(/*previousWord->data.terminated ? '\n' : */' '); 90 | i->data.appendTo(text); 91 | //previousWord = i; 92 | } 93 | } 94 | 95 | void Word::appendTo(String& text) const 96 | { 97 | if(flags & quotedFlag) 98 | { 99 | text.append('"'); 100 | text.append(*this); 101 | text.append('"'); 102 | } 103 | else // TODO: escape spaces using blackslashes 104 | text.append(*this); 105 | } 106 | 107 | void Word::splitLines(const String& text, List& words) 108 | { 109 | const char* str = text.getData(); 110 | for(;;) 111 | { 112 | const char* end = str; 113 | for(; *end; ++end) 114 | if(*end == '\n' || *end == '\r') 115 | break; 116 | // TODO: read escaped spaces as ordinary spaces 117 | words.append(Word(text.substr(str - text.getData(), end - str), 0)); 118 | str = end; 119 | if(*str) 120 | { 121 | if(*str == '\r' && str[1] == '\n') 122 | str += 2; 123 | else 124 | ++str; 125 | } 126 | else 127 | break; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /compile.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | set MARE_BUILD_DIR="build/Debug/mare" 5 | set MARE_OUTPUT_DIR="build/Debug/mare" 6 | set MARE_SOURCE_DIR="src" 7 | set MARE_SOURCE_FILES=mare/Generator.cpp mare/CMake.cpp mare/CodeBlocks.cpp mare/CodeLite.cpp mare/JsonDb.cpp mare/Main.cpp mare/Make.cpp mare/Mare.cpp mare/NetBeans.cpp mare/Vcproj.cpp mare/Vcxproj.cpp mare/Tools/md5.cpp mare/Tools/Win32/getopt.cpp libmare/Engine.cpp libmare/Namespace.cpp libmare/Parser.cpp libmare/Statement.cpp libmare/Tools/Directory.cpp libmare/Tools/Error.cpp libmare/Tools/File.cpp libmare/Tools/Process.cpp libmare/Tools/Scope.cpp libmare/Tools/String.cpp libmare/Tools/Word.cpp 8 | 9 | :main 10 | goto get_args 11 | :get_args_return 12 | goto find_visual_studio 13 | :find_visual_studio_return 14 | goto build 15 | :build_return 16 | goto end 17 | 18 | 19 | :get_args 20 | if "%1" == "--buildDir" ( 21 | set MARE_BUILD_DIR="%2" 22 | shift /1 23 | shift /1 24 | goto get_args 25 | ) 26 | if "%1" == "--outputDir" ( 27 | set MARE_OUTPUT_DIR="%2" 28 | shift /1 29 | shift /1 30 | goto get_args 31 | ) 32 | if "%1" == "--sourceDir" ( 33 | set MARE_SOURCE_DIR="%2" 34 | shift /1 35 | shift /1 36 | goto get_args 37 | ) 38 | if "%1" == "" goto get_args_return 39 | goto usage 40 | 41 | 42 | :usage 43 | echo Usage: %0 [^-^-buildDir=^] [^-^-outputDir=^] [^-^-sourceDir=^] 44 | goto end 45 | 46 | 47 | :find_visual_studio 48 | if not "%VCINSTALLDIR%"=="" goto find_visual_studio_return 49 | 50 | FOR /F "usebackq tokens=*" %%A IN (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) DO ( 51 | if exist "%%A" ( 52 | call "%%A\VC\Auxiliary\Build\vcvars32.bat" 53 | goto find_visual_studio_return 54 | ) 55 | ) 56 | 57 | FOR /F "usebackq skip=2 tokens=2*" %%A IN (`reg query HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7 /v 15.0 2^>nul`) DO ( 58 | if exist "%%B" ( 59 | call "%%B\VC\Auxiliary\Build\vcvars32.bat" 60 | goto find_visual_studio_return 61 | ) 62 | ) 63 | 64 | if not "%VS140COMNTOOLS%"=="" ( 65 | call "%VS140COMNTOOLS%vsvars32.bat" 66 | goto find_visual_studio_return 67 | ) 68 | 69 | if not "%VS120COMNTOOLS%"=="" ( 70 | call "%VS120COMNTOOLS%vsvars32.bat" 71 | goto find_visual_studio_return 72 | ) 73 | 74 | if not "%VS110COMNTOOLS%"=="" ( 75 | call "%VS110COMNTOOLS%vsvars32.bat" 76 | goto find_visual_studio_return 77 | ) 78 | 79 | if not "%VS100COMNTOOLS%"=="" ( 80 | call "%VS100COMNTOOLS%vsvars32.bat" 81 | goto find_visual_studio_return 82 | ) 83 | 84 | echo "error: Could not find Visual Studio installation." 85 | goto end 86 | 87 | 88 | :build 89 | if not exist %MARE_SOURCE_DIR:"=%/mare/Main.cpp ( 90 | echo "error: Could not find source files." 91 | goto end 92 | ) 93 | setlocal enableextensions 94 | if not exist %MARE_BUILD_DIR% mkdir %MARE_BUILD_DIR% 95 | if not exist %MARE_OUTPUT_DIR% mkdir %MARE_OUTPUT_DIR% 96 | endlocal 97 | setlocal 98 | set DEL_FILE=%MARE_OUTPUT_DIR:"=%/mare.exe 99 | del %DEL_FILE:/=\% 2>NUL 100 | endlocal 101 | for %%f in (%MARE_SOURCE_FILES%) do ( 102 | cl /I"%MARE_SOURCE_DIR:"=%/libmare" /Zi /nologo /W3 /WX- /Od /Oy- /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /EHsc /RTC1 /GS /fp:precise /Zc:wchar_t /Zc:forScope /Fd"%MARE_BUILD_DIR:"=%/" /Fo"%MARE_BUILD_DIR:"=%/" /Gd /analyze- /c "%MARE_SOURCE_DIR:"=%/%%f" 103 | ) 104 | set MARE_OBJECTS= 105 | for %%f in (%MARE_BUILD_DIR:"=%/*.obj) do ( 106 | call set MARE_OBJECTS=%%MARE_OBJECTS%% %MARE_BUILD_DIR:"=%/%%f 107 | ) 108 | echo ^-^> %MARE_BUILD_DIR:"=%/mare.exe 109 | link /OUT:"%MARE_OUTPUT_DIR:"=%/mare.exe" /NOLOGO "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 %MARE_OBJECTS% 110 | rem if exist %MARE_OUTPUT_DIR:"=%/mare.exe (echo done) else echo failed 111 | goto build_return 112 | 113 | 114 | :end 115 | endlocal 116 | -------------------------------------------------------------------------------- /src/libmare/Tools/List.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | template class List 5 | { 6 | public: 7 | class Node 8 | { 9 | public: 10 | T data; 11 | 12 | Node() {} 13 | Node(const T& data) : data(data) {} 14 | 15 | inline Node* getNext() {return next;} 16 | inline const Node* getNext() const {return next;} 17 | inline Node* getPrevious() {return previous;} 18 | inline const Node* getPrevious() const {return previous;} 19 | 20 | private: 21 | Node* next; 22 | Node* previous; 23 | 24 | friend class List; 25 | }; 26 | 27 | List() : first(0), last(0), size(0), firstFree(0) {} 28 | 29 | ~List() 30 | { 31 | clear(); 32 | for(Node* node = firstFree, * next; node; node = next) 33 | { 34 | next = node->next; 35 | delete node; 36 | } 37 | } 38 | 39 | List& operator=(const List& other) 40 | { 41 | clear(); 42 | for(Node* i = other.first; i; i = i->next) 43 | append(i->data); 44 | return *this; 45 | } 46 | 47 | T& append(const T& data = T()) 48 | { 49 | Node* node; 50 | if(firstFree) 51 | { 52 | node = firstFree; 53 | firstFree = firstFree->next; 54 | node->data = data; 55 | } 56 | else 57 | node = new Node(data); 58 | node->next = 0; 59 | if((node->previous = last)) 60 | last->next = node; 61 | else 62 | first = node; 63 | last = node; 64 | ++size; 65 | return node->data; 66 | } 67 | 68 | T& prepend(const T& data = T()) 69 | { 70 | Node* node; 71 | if(firstFree) 72 | { 73 | node = firstFree; 74 | firstFree = firstFree->next; 75 | node->data = data; 76 | } 77 | else 78 | node = new Node(data); 79 | node->previous = 0; 80 | if((node->next = first)) 81 | first->previous = node; 82 | else 83 | last = node; 84 | first = node; 85 | ++size; 86 | return node->data; 87 | } 88 | 89 | void remove(Node* node) 90 | { 91 | if(node->next) 92 | node->next->previous = node->previous; 93 | else 94 | last = node->previous; 95 | if(node->previous) 96 | node->previous->next = node->next; 97 | else 98 | first = node->next; 99 | --size; 100 | node->next = firstFree; 101 | firstFree = node; 102 | } 103 | 104 | inline void removeFirst() {remove(getFirst());} 105 | inline void removeLast() {remove(getLast());} 106 | 107 | void clear() 108 | { 109 | if(first) 110 | { 111 | last->next = firstFree; 112 | firstFree = first; 113 | first = last = 0; 114 | size = 0; 115 | } 116 | } 117 | 118 | inline Node* getFirst() {return first;} 119 | inline const Node* getFirst() const {return first;} 120 | inline Node* getLast() {return last;} 121 | inline const Node* getLast() const {return last;} 122 | 123 | inline unsigned int getSize() const {return size;} 124 | 125 | inline bool isEmpty() const {return first == 0;} 126 | 127 | /** 128 | * Sorts the list using the comparator \c cmp. 129 | * Note: This function may change the data element of an existing node. 130 | * @param The comparator used to compare two data elements. 131 | */ 132 | void sort(int (*cmp)(const T& a, const T& b)) 133 | { 134 | if(first == last) 135 | return; 136 | struct QuickSort 137 | { 138 | int (*compare)(const T& a, const T& b); 139 | inline static void swap(Node* a, Node* b) 140 | { 141 | T tmp = a->data; 142 | a->data = b->data; 143 | b->data = tmp; 144 | } 145 | inline void sort(Node* left, Node* right) 146 | { 147 | Node* ptr0, * ptr1, * ptr2; 148 | ptr0 = ptr1 = ptr2 = left; 149 | const T& pivot = left->data; 150 | do 151 | { 152 | ptr2 = ptr2->next; 153 | if(compare(ptr2->data, pivot) < 0) 154 | { 155 | ptr0 = ptr1; 156 | ptr1 = ptr1->next; 157 | swap(ptr1, ptr2); 158 | } 159 | } while(ptr2 != right); 160 | swap(left, ptr1); 161 | if(ptr1 != right) 162 | ptr1 = ptr1->next; 163 | if(left != ptr0) 164 | sort(left, ptr0); 165 | if(ptr1 != right) 166 | sort(ptr1, right); 167 | } 168 | } a; 169 | a.compare = cmp; 170 | a.sort(first, last); 171 | } 172 | 173 | private: 174 | Node* first; 175 | Node* last; 176 | unsigned int size; 177 | Node* firstFree; 178 | }; 179 | 180 | -------------------------------------------------------------------------------- /src/libmare/Statement.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "Tools/Assert.h" 4 | #include "Statement.h" 5 | #include "Namespace.h" 6 | #include "Engine.h" 7 | 8 | void BlockStatement::execute(Namespace& space) 9 | { 10 | for(List::Node* i = statements.getFirst(); i; i = i->getNext()) 11 | i->data->execute(space); 12 | } 13 | 14 | void WrapperStatement::execute(Namespace& space) 15 | { 16 | statement->execute(space); 17 | } 18 | 19 | void AssignStatement::execute(Namespace& space) 20 | { 21 | space.addKey(variable, flags, value, operation); 22 | } 23 | 24 | void RemoveStatement::execute(Namespace& space) 25 | { 26 | space.removeKey(variable); 27 | } 28 | 29 | void BinaryStatement::execute(Namespace& space) 30 | { 31 | switch(operation) 32 | { 33 | case Token::plus: 34 | leftOperand->execute(space); 35 | rightOperand->execute(space); 36 | break; 37 | case Token::minus: 38 | { 39 | leftOperand->execute(space); 40 | Namespace* rightSpace = new Namespace(space.getEngine(), &space, &space.getEngine(), rightOperand, 0, 0); 41 | space.removeKeysRaw(*rightSpace); 42 | delete rightSpace; 43 | } 44 | break; 45 | case Token::and_: 46 | case Token::or_: 47 | case Token::equal: 48 | case Token::notEqual: 49 | case Token::greaterThan: 50 | case Token::lowerThan: 51 | case Token::greaterEqualThan: 52 | case Token::lowerEqualThan: 53 | { 54 | Namespace* leftSpace = new Namespace(space.getEngine(), &space, &space.getEngine(), leftOperand, 0, 0); 55 | Namespace* rightSpace = new Namespace(space.getEngine(), &space, &space.getEngine(), rightOperand, 0, 0); 56 | bool result = false; 57 | switch(operation) 58 | { 59 | case Token::and_: 60 | result = !leftSpace->getFirstKey().isEmpty() && !rightSpace->getFirstKey().isEmpty(); 61 | break; 62 | case Token::or_: 63 | result = !leftSpace->getFirstKey().isEmpty() || !rightSpace->getFirstKey().isEmpty(); 64 | break; 65 | case Token::equal: 66 | leftSpace->compareKeys(*rightSpace, result); 67 | break; 68 | case Token::notEqual: 69 | if(leftSpace->compareKeys(*rightSpace, result)) 70 | result = !result; 71 | break; 72 | case Token::greaterThan: 73 | { 74 | int val; 75 | if(leftSpace->versionCompareKeys(*rightSpace, val)) 76 | result = val > 0; 77 | } 78 | break; 79 | case Token::lowerThan: 80 | { 81 | int val; 82 | if(leftSpace->versionCompareKeys(*rightSpace, val)) 83 | result = val < 0; 84 | } 85 | break; 86 | case Token::greaterEqualThan: 87 | { 88 | int val; 89 | if(leftSpace->versionCompareKeys(*rightSpace, val)) 90 | result = val >= 0; 91 | } 92 | break; 93 | case Token::lowerEqualThan: 94 | { 95 | int val; 96 | if(leftSpace->versionCompareKeys(*rightSpace, val)) 97 | result = val <= 0; 98 | } 99 | break; 100 | default: 101 | ASSERT(false); 102 | break; 103 | } 104 | delete leftSpace; 105 | delete rightSpace; 106 | if(result) 107 | space.addKeyRaw(Word("true", 0), 0); 108 | } 109 | break; 110 | 111 | default: 112 | ASSERT(false); 113 | break; 114 | } 115 | } 116 | 117 | void StringStatement::execute(Namespace& space) 118 | { 119 | space.addKey(value, 0, 0); 120 | } 121 | 122 | void ReferenceStatement::execute(Namespace& space) 123 | { 124 | Word* word; 125 | Namespace* ref; 126 | if(space.getEngine().resolveScript(variable, word, ref)) 127 | if(ref && ref->statement) 128 | { 129 | ASSERT(!(ref->flags & Namespace::compilingFlag)); 130 | ref->flags |= Namespace::compilingFlag; 131 | ref->statement->execute(space); 132 | ref->flags &= ~Namespace::compilingFlag; 133 | } 134 | } 135 | 136 | void IfStatement::execute(Namespace& space) 137 | { 138 | Namespace* condSpace = new Namespace(space.getEngine(), &space, &space.getEngine(), condition, 0, 0); 139 | bool cond = !condSpace->getFirstKey().isEmpty(); 140 | delete condSpace; 141 | if(cond) 142 | thenStatements->execute(space); 143 | else if(elseStatements) 144 | elseStatements->execute(space); 145 | } 146 | 147 | void UnaryStatement::execute(Namespace& space) 148 | { 149 | switch(operation) 150 | { 151 | case Token::not_: 152 | { 153 | Namespace* opSpace = new Namespace(space.getEngine(), &space, &space.getEngine(), operand, 0, 0); 154 | bool result = opSpace->getFirstKey().isEmpty(); 155 | delete opSpace; 156 | if(result) 157 | space.addKeyRaw(Word("true", 0), 0); 158 | } 159 | break; 160 | default: 161 | ASSERT(false); 162 | break; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/libmare/Engine.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Tools/Assert.h" 3 | #include "Engine.h" 4 | #include "Namespace.h" 5 | #include "Parser.h" 6 | 7 | bool Engine::load(const String& file) 8 | { 9 | if(currentSpace) 10 | return false; 11 | Parser parser(*this); 12 | ASSERT(!rootStatement); 13 | rootStatement = parser.parse(file, errorHandler, errorUserData); 14 | if(!rootStatement) 15 | return false; 16 | currentSpace = new Namespace(*this, 0, this, 0, 0, 0); 17 | return true; 18 | } 19 | 20 | void Engine::error(const String& message) 21 | { 22 | errorHandler(errorUserData, String(), -1, message); 23 | } 24 | 25 | bool Engine::hasKey(const String& key, bool allowInheritance) 26 | { 27 | if(!currentSpace->enterKey(key, allowInheritance)) 28 | return false; 29 | return true; 30 | } 31 | 32 | bool Engine::enterKey(const String& key, bool allowInheritance) 33 | { 34 | Namespace* subSpace = currentSpace->enterKey(key, allowInheritance); 35 | if(subSpace) 36 | { 37 | currentSpace = subSpace; 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | void Engine::enterUnnamedKey() 44 | { 45 | Namespace* subSpace = currentSpace->enterUnnamedKey(0); 46 | ASSERT(subSpace); 47 | currentSpace = subSpace; 48 | } 49 | 50 | void Engine::enterNewKey(const String& key) 51 | { 52 | Namespace* subSpace = currentSpace->enterNewKey(key); 53 | ASSERT(subSpace); 54 | currentSpace = subSpace; 55 | } 56 | 57 | void Engine::enterRootKey() 58 | { 59 | Namespace* subSpace = currentSpace->enterUnnamedKey(rootStatement); 60 | ASSERT(subSpace); 61 | currentSpace = subSpace; 62 | } 63 | 64 | String Engine::getKeyOrigin(const String& key) 65 | { 66 | return currentSpace->getKeyOrigin(key); 67 | } 68 | 69 | bool Engine::resolveScript(const String& key, Word*& word, Namespace*& result) 70 | { 71 | for(Namespace* space = currentSpace->getParent(); space; space = space->getParent()) 72 | if(space->resolveScript2(key, word, result)) 73 | return true; 74 | return false; 75 | } 76 | 77 | bool Engine::resolveScript(const String& key, Namespace* excludeStatements, Word*& word, Namespace*& result) 78 | { 79 | for(Namespace* space = currentSpace->getParent(); space; space = space->getParent()) 80 | if(space->resolveScript2(key, excludeStatements, word, result)) 81 | return true; 82 | return false; 83 | } 84 | 85 | bool Engine::leaveKey() 86 | { 87 | if(!currentSpace->getParent()) 88 | return false; 89 | Namespace* parent = currentSpace->getParent(); 90 | if(currentSpace->flags & Namespace::unnamedFlag) 91 | delete currentSpace; 92 | currentSpace = parent; 93 | return true; 94 | } 95 | 96 | void Engine::getKeys(List& keys) 97 | { 98 | currentSpace->getKeys(keys); 99 | } 100 | 101 | void Engine::appendKeys(String& output) 102 | { 103 | return currentSpace->appendKeys(output); 104 | } 105 | 106 | bool Engine::getKeys(const String& key, List& keys, bool allowInheritance) 107 | { 108 | if(enterKey(key, allowInheritance)) 109 | { 110 | currentSpace->getKeys(keys); 111 | leaveKey(); 112 | return true; 113 | } 114 | return false; 115 | } 116 | 117 | String Engine::getFirstKey() 118 | { 119 | return currentSpace->getFirstKey(); 120 | } 121 | 122 | String Engine::getFirstKey(const String& key, bool allowInheritance) 123 | { 124 | if(enterKey(key, allowInheritance)) 125 | { 126 | String result = currentSpace->getFirstKey(); 127 | leaveKey(); 128 | return result; 129 | } 130 | return String(); 131 | } 132 | 133 | void Engine::getText(List& text) 134 | { 135 | currentSpace->getText(text); 136 | } 137 | 138 | bool Engine::getText(const String& key, List& text, bool allowInheritance) 139 | { 140 | if(enterKey(key, allowInheritance)) 141 | { 142 | currentSpace->getText(text); 143 | leaveKey(); 144 | return true; 145 | } 146 | return false; 147 | } 148 | 149 | String Engine::getMareDir() const 150 | { 151 | return currentSpace->getMareDir(); 152 | } 153 | 154 | void Engine::addDefaultKey(const String& key) 155 | { 156 | currentSpace->addDefaultKey(key); 157 | } 158 | 159 | void Engine::addDefaultKey(const String& key, const String& value) 160 | { 161 | currentSpace->addDefaultKey(key, Word::defaultFlag, value); 162 | } 163 | 164 | void Engine::addDefaultKey(const String& key, const Map& value) 165 | { 166 | currentSpace->addDefaultKey(key, Word::defaultFlag, value); 167 | } 168 | 169 | void Engine::addCommandLineKey(const String& key, const String& value) 170 | { 171 | currentSpace->addDefaultKey(key, Word::commandLineFlag, value); 172 | } 173 | 174 | void Engine::setKey(const Word& key) 175 | { 176 | currentSpace->setKeyRaw(key); 177 | } 178 | 179 | void Engine::pushAndLeaveKey() 180 | { 181 | stashedKeys.append(currentSpace); 182 | currentSpace = currentSpace->getParent(); 183 | } 184 | 185 | bool Engine::popKey() 186 | { 187 | if(stashedKeys.isEmpty()) 188 | return false; 189 | currentSpace = stashedKeys.getLast()->data; 190 | stashedKeys.removeLast(); 191 | return true; 192 | } 193 | -------------------------------------------------------------------------------- /src/mare/Vcxproj.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/Map.h" 5 | #include "Tools/String.h" 6 | #include "Tools/File.h" 7 | 8 | class Engine; 9 | 10 | class Vcxproj 11 | { 12 | public: 13 | 14 | Vcxproj(Engine& engine, int version) : engine(engine), version(version) {} 15 | 16 | bool generate(const Map& userArgs); 17 | 18 | private: 19 | 20 | class Config 21 | { 22 | public: 23 | String name; 24 | String platform; 25 | 26 | Config(const String& name, const String& platform) : name(name), platform(platform) {} 27 | }; 28 | 29 | class Project 30 | { 31 | public: 32 | class Config 33 | { 34 | public: 35 | String name; /**< The name of the configuration without the platform extension */ 36 | String platform; 37 | 38 | String type; 39 | enum Language 40 | { 41 | CPP, 42 | C, 43 | } language; 44 | 45 | List buildCommand; /**< For Makefile projects */ 46 | List reBuildCommand; /**< For Makefile projects */ 47 | List cleanCommand; /**< For Makefile projects */ 48 | List preBuildCommand; 49 | List preLinkCommand; 50 | List postBuildCommand; 51 | String buildDir; 52 | List command; 53 | List message; 54 | String firstOutput; 55 | List outputs; 56 | List inputs; 57 | List dependencies; 58 | List cppFlags; 59 | List cFlags; 60 | Map compilerFlags; 61 | Map linkFlags; 62 | List defines; 63 | List includePaths; 64 | List libPaths; 65 | List libs; 66 | 67 | Map outputBasenames; 68 | 69 | Map cppOptions; 70 | Map linkOptions; 71 | Map vsOptions; 72 | 73 | Config(const String& name, const String& platform) : name(name), platform(platform), language(CPP) {} 74 | }; 75 | 76 | class File 77 | { 78 | public: 79 | class Config 80 | { 81 | public: 82 | List message; 83 | List command; 84 | List outputs; 85 | List inputs; 86 | List dependencies; 87 | bool hasCppFlags; 88 | List cppFlags; 89 | bool hasCFlags; 90 | List cFlags; 91 | 92 | Map cppOptions; 93 | }; 94 | 95 | String type; 96 | String filter; 97 | Map configs; 98 | bool useProjectCompilerFlags; 99 | 100 | File() : useProjectCompilerFlags(true) {} 101 | }; 102 | 103 | String name; 104 | String displayName; 105 | String projectFile; 106 | String projectDir; 107 | String guid; 108 | String filter; 109 | Map configs; 110 | Map files; 111 | Map dependencies; 112 | List root; 113 | List visualizers; 114 | 115 | Project(const String& name) : name(name) {} 116 | }; 117 | 118 | class ProjectFilter 119 | { 120 | public: 121 | String guid; 122 | List projects; 123 | List filters; 124 | 125 | ProjectFilter(const String& guid) : guid(guid) {} 126 | }; 127 | 128 | class OptionGroup 129 | { 130 | public: 131 | String name; 132 | String unsetValue; 133 | String paramName; 134 | 135 | OptionGroup(const String& name, const String& unsetValue = String(), const String& paramName = String()) : name(name), unsetValue(unsetValue), paramName(paramName) {} 136 | }; 137 | 138 | class Option 139 | { 140 | public: 141 | OptionGroup* group; 142 | String value; 143 | 144 | Option() : group(0) {} 145 | 146 | Option(OptionGroup* group, const String& value) : group(group), value(value) {} 147 | 148 | bool hasParam(const String& option) const; 149 | static String getParamValue(const String& option); 150 | }; 151 | 152 | class OptionMap : public Map 153 | { 154 | public: 155 | Node* find(const String& key); 156 | }; 157 | 158 | Engine& engine; 159 | int version; /**< Version of the vcxproj file format e.g. 2010 */ 160 | 161 | File file; 162 | 163 | String solutionName; 164 | String solutionFile; 165 | String solutionDir; 166 | Map configs; 167 | Map projects; 168 | Map projectFilters; 169 | 170 | List knownOptionGroups; 171 | OptionMap knownCppOptions; 172 | Map knownLinkOptions; 173 | OptionMap knownVsOptions; 174 | 175 | String openedFile; /**< The file that is currently written */ 176 | 177 | bool readFile(); 178 | 179 | bool processData(); 180 | bool resolveDependencies(); 181 | 182 | bool generateSln(); 183 | bool generateVcxprojs(); 184 | bool generateVcxproj(Project& project); 185 | bool generateVcxprojFilter(Project& project); 186 | 187 | void fileOpen(const String& name); 188 | void fileWrite(const String& data); 189 | void fileClose(); 190 | 191 | static String relativePath(const String& projectDir, const String& path); 192 | static String createSomethingLikeGUID(const String& name); 193 | static String join(const List& items, char sep = ';', const String& suffix = String()); 194 | static String joinPaths(const String& projectDir, const List& paths); 195 | static String joinCommands(const List& commands); 196 | static String xmlEscape(const String& text); 197 | }; 198 | 199 | -------------------------------------------------------------------------------- /src/mare/Vcproj.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Tools/Map.h" 5 | #include "Tools/String.h" 6 | #include "Tools/File.h" 7 | 8 | class Engine; 9 | 10 | class Vcproj 11 | { 12 | public: 13 | 14 | Vcproj(Engine& engine, int version) : engine(engine), version(version) {} 15 | 16 | bool generate(const Map& userArgs); 17 | 18 | private: 19 | 20 | class Config 21 | { 22 | public: 23 | String name; 24 | String platform; 25 | 26 | Config(const String& name, const String& platform) : name(name), platform(platform) {} 27 | }; 28 | 29 | class Project 30 | { 31 | public: 32 | class Config 33 | { 34 | public: 35 | String name; /**< The name of the configuration without the platform extension */ 36 | String platform; 37 | 38 | String type; 39 | enum Language 40 | { 41 | CPP, 42 | C, 43 | } language; 44 | 45 | List buildCommand; /**< For Makefile projects */ 46 | List reBuildCommand; /**< For Makefile projects */ 47 | List cleanCommand; /**< For Makefile projects */ 48 | List preBuildCommand; 49 | List preLinkCommand; 50 | List postBuildCommand; 51 | String buildDir; 52 | List command; 53 | List message; 54 | String firstOutput; 55 | List outputs; 56 | List inputs; 57 | List dependencies; 58 | List cppFlags; 59 | List cFlags; 60 | Map compilerFlags; 61 | Map linkFlags; 62 | List defines; 63 | List includePaths; 64 | List libPaths; 65 | List libs; 66 | 67 | Map cppOptions; 68 | Map linkOptions; 69 | Map librarianOptions; 70 | Map vsOptions; 71 | 72 | Config(const String& name, const String& platform) : name(name), platform(platform), language(CPP) {} 73 | }; 74 | 75 | class File 76 | { 77 | public: 78 | class Config 79 | { 80 | public: 81 | List message; 82 | List command; 83 | List outputs; 84 | List inputs; 85 | List dependencies; 86 | bool hasCppFlags; 87 | List cppFlags; 88 | bool hasCFlags; 89 | List cFlags; 90 | 91 | Map cppOptions; 92 | }; 93 | 94 | String type; 95 | String path; 96 | String filter; 97 | Map configs; 98 | bool useProjectCompilerFlags; 99 | 100 | File() : useProjectCompilerFlags(true) {} 101 | }; 102 | 103 | String name; 104 | String displayName; 105 | String projectDir; 106 | String projectFile; 107 | String guid; 108 | String filter; 109 | Map configs; 110 | Map files; 111 | Map dependencies; 112 | List root; 113 | 114 | Project(const String& name) : name(name) {} 115 | }; 116 | 117 | class ProjectFilter 118 | { 119 | public: 120 | String guid; 121 | List projects; 122 | List filters; 123 | 124 | ProjectFilter(const String& guid) : guid(guid) {} 125 | }; 126 | 127 | class OptionGroup 128 | { 129 | public: 130 | String name; 131 | String unsetValue; 132 | String paramName; 133 | 134 | OptionGroup(const String& name, const String& unsetValue = String(), const String& paramName = String()) : name(name), unsetValue(unsetValue), paramName(paramName) {} 135 | }; 136 | 137 | class Option 138 | { 139 | public: 140 | OptionGroup* group; 141 | String value; 142 | 143 | Option() : group(0) {} 144 | 145 | Option(OptionGroup* group, const String& value) : group(group), value(value) {} 146 | 147 | bool hasParam(const String& option) const; 148 | static String getParamValue(const String& option); 149 | }; 150 | 151 | class OptionMap : public Map 152 | { 153 | public: 154 | Node* find(const String& key); 155 | }; 156 | 157 | class Filter 158 | { 159 | public: 160 | String name; 161 | List files; 162 | List filters; 163 | 164 | void write(Vcproj& vc, const Project& project, const String& tabs = String()); 165 | }; 166 | 167 | Engine& engine; 168 | int version; /**< Version of the vcxproj file format e.g. 2010 */ 169 | 170 | File file; 171 | 172 | String solutionName; 173 | String solutionFile; 174 | String solutionDir; 175 | List platforms; 176 | Map configs; 177 | Map projects; 178 | Map projectFilters; 179 | 180 | List knownOptionGroups; 181 | OptionMap knownCppOptions; 182 | Map knownLinkOptions; 183 | OptionMap knownVsOptions; 184 | 185 | String openedFile; /**< The file that is currently written */ 186 | 187 | bool readFile(); 188 | 189 | bool processData(); 190 | bool resolveDependencies(); 191 | 192 | bool generateSln(); 193 | bool generateVcprojs(); 194 | bool generateVcproj(const Project& project); 195 | bool generateVcprojFiles(const Project& project); 196 | 197 | void fileOpen(const String& name); 198 | void fileWrite(const String& data); 199 | void fileClose(); 200 | 201 | static String createSomethingLikeGUID(const String& name); 202 | static String relativePath(const String& projectDir, const String& path); 203 | static String join(const List& items, char sep = ';', const String& suffix = String()); 204 | static String joinPaths(const String& projectDir, const List& paths); 205 | static String joinCommands(const List& commands); 206 | static String xmlEscape(const String& text); 207 | }; 208 | 209 | -------------------------------------------------------------------------------- /src/mare/Generator.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Engine.h" 7 | 8 | #include "Tools/Assert.h" 9 | #include "Tools/Error.h" 10 | #include "Tools/Word.h" 11 | 12 | #include "Generator.h" 13 | 14 | bool Generator::generate(const Map& userArgs) 15 | { 16 | addDefaultKeys(engine); 17 | 18 | // add user arguments 19 | for(const Map::Node* i = userArgs.getFirst(); i; i = i->getNext()) 20 | engine.addCommandLineKey(i->key, i->data); 21 | 22 | // step #1: read input file 23 | Data data; 24 | if(!readFile(data)) 25 | return false; 26 | 27 | // step #2: ... 28 | if(!processData(data)) 29 | return false; 30 | 31 | // step #3: generate workspace and project files 32 | if(!writeFiles()) 33 | return false; 34 | 35 | return true; 36 | } 37 | 38 | bool Generator::readFile(Data& data) 39 | { 40 | // get some global keys 41 | engine.enterRootKey(); 42 | data.name = engine.getFirstKey("name"); 43 | List allPlatforms, allConfigurations, allTargets; 44 | engine.getKeys("platforms", allPlatforms); 45 | engine.getKeys("configurations", allConfigurations); 46 | engine.getKeys("targets", allTargets); 47 | engine.leaveKey(); 48 | 49 | // do something for each target in each configuration 50 | for(const List::Node* i = allPlatforms.getFirst(); i; i = 0) // just use the first platform since Generator does not really support multiple target platforms 51 | { 52 | const String& platformName = i->data; 53 | Platform& platform = data.platforms.append(platformName); 54 | 55 | for(const List::Node* i = allConfigurations.getFirst(); i; i = i->getNext()) 56 | { 57 | const String& configName = i->data; 58 | Configuration& configuration = platform.configurations.append(configName); 59 | 60 | for(const List::Node* i = allTargets.getFirst(); i; i = i->getNext()) 61 | { 62 | const String& targetName = i->data; 63 | 64 | engine.enterUnnamedKey(); 65 | engine.addDefaultKey("platform", platformName); 66 | engine.addDefaultKey(platformName, platformName); 67 | engine.addDefaultKey("configuration", configName); 68 | engine.addDefaultKey(configName, configName); 69 | engine.addDefaultKey("target", targetName); 70 | //engine.addDefaultKey(targetName, targetName); 71 | engine.enterRootKey(); 72 | VERIFY(engine.enterKey("targets")); 73 | if(!engine.enterKey(i->data)) 74 | { 75 | engine.error(String().format(256, "cannot find target \"%s\"", i->data.getData())); 76 | return false; 77 | } 78 | engine.addDefaultKey("mareDir", engine.getMareDir()); 79 | 80 | Target& target = configuration.targets.append(targetName); 81 | 82 | target.folder = engine.getFirstKey("folder", false); 83 | 84 | engine.getText("buildCommand", target.buildCommand, false); 85 | engine.getText("reBuildCommand", target.reBuildCommand, false); 86 | engine.getText("cleanCommand", target.cleanCommand, false); 87 | 88 | engine.getText("preBuildCommand", target.preBuildCommand, false); 89 | engine.getText("preLinkCommand", target.preLinkCommand, false); 90 | engine.getText("postBuildCommand", target.postBuildCommand, false); 91 | 92 | target.buildDir = engine.getFirstKey("buildDir", true); 93 | 94 | engine.getText("command", target.command, false); 95 | engine.getText("message", target.message, false); 96 | engine.getKeys("output", target.output, false); 97 | engine.getKeys("input", target.input, false); 98 | 99 | engine.getKeys("dependencies", target.dependencies, false); 100 | 101 | engine.getKeys("cppFlags", target.cppFlags, true); 102 | engine.getKeys("cFlags", target.cFlags, true); 103 | engine.getKeys("linkFlags", target.linkFlags, true); 104 | engine.getKeys("defines", target.defines, true); 105 | engine.getKeys("includePaths", target.includePaths, true); 106 | engine.getKeys("libPaths", target.libPaths, true); 107 | engine.getKeys("libs", target.libs, true); 108 | 109 | engine.getKeys("root", target.root, true); 110 | 111 | engine.getKeys("cppCompiler", target.cppCompiler, true); 112 | engine.getKeys("cCompiler", target.cCompiler, true); 113 | 114 | if(engine.enterKey("files")) 115 | { 116 | List files; 117 | engine.getKeys(files); 118 | for(const List::Node* i = files.getFirst(); i; i = i->getNext()) 119 | { 120 | const String& fileName = i->data; 121 | File& file = target.files.append(fileName); 122 | engine.enterUnnamedKey(); 123 | engine.addDefaultKey("file", fileName); 124 | VERIFY(engine.enterKey(fileName)); 125 | file.folder = engine.getFirstKey("folder", false); 126 | 127 | engine.getText("command", file.command, false); 128 | engine.getText("message", file.message, false); 129 | engine.getKeys("output", file.output, false); 130 | engine.getKeys("input", file.input, false); 131 | engine.getKeys("dependencies", file.dependencies, false); 132 | file.hasCppFlags = engine.getKeys("cppFlags", file.cppFlags, false); 133 | file.hasCFlags = engine.getKeys("cFlags", file.cFlags, false); 134 | 135 | engine.leaveKey(); // VERIFY(engine.enterKey(i->data)); 136 | engine.leaveKey(); 137 | } 138 | 139 | engine.leaveKey(); 140 | } 141 | 142 | engine.leaveKey(); 143 | engine.leaveKey(); 144 | engine.leaveKey(); 145 | engine.leaveKey(); 146 | } 147 | } 148 | } 149 | 150 | return true; 151 | } 152 | 153 | void Generator::fileOpen(const String& name) 154 | { 155 | if(!file.open(name, ::File::writeFlag)) 156 | { 157 | engine.error(Error::getString()); 158 | exit(EXIT_FAILURE); 159 | } 160 | openedFile = name; 161 | } 162 | 163 | void Generator::fileWrite(const String& data) 164 | { 165 | if(!file.write(data)) 166 | { 167 | engine.error(Error::getString()); 168 | exit(EXIT_FAILURE); 169 | } 170 | } 171 | 172 | void Generator::fileClose() 173 | { 174 | file.close(); 175 | if(!openedFile.isEmpty()) 176 | { 177 | puts(openedFile.getData()); 178 | fflush(stdout); 179 | } 180 | openedFile.clear(); 181 | } 182 | 183 | void Generator::error(const String& message) 184 | { 185 | engine.error(message); 186 | } 187 | -------------------------------------------------------------------------------- /src/mare/Tools/md5.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This code implements the MD5 message-digest algorithm. 3 | * The algorithm is due to Ron Rivest. This code was 4 | * written by Colin Plumb in 1993, no copyright is claimed. 5 | * This code is in the public domain; do with it what you wish. 6 | * 7 | * Equivalent code is available from RSA Data Security, Inc. 8 | * This code has been tested against that, and is equivalent, 9 | * except that you don't need to include two pages of legalese 10 | * with every copy. 11 | * 12 | * To compute the message digest of a chunk of bytes, declare an 13 | * MD5Context structure, pass it to MD5Init, call MD5Update as 14 | * needed on buffers full of bytes, and then call MD5Final, which 15 | * will fill a supplied 16-byte array with the digest. 16 | */ 17 | 18 | #include /* for memcpy() */ 19 | 20 | #include "md5.h" 21 | 22 | #define ctx this // quick and dirty c++ port ^^ 23 | typedef unsigned int uint32; 24 | 25 | #ifndef HIGHFIRST 26 | #define byteReverse(buf, len) /* Nothing */ 27 | #else 28 | /* 29 | * Note: this code is harmless on little-endian machines. 30 | */ 31 | static void byteReverse(unsigned char *buf, unsigned longs) 32 | { 33 | uint32 t; 34 | do { 35 | t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | 36 | ((unsigned) buf[1] << 8 | buf[0]); 37 | *(uint32 *) buf = t; 38 | buf += 4; 39 | } while (--longs); 40 | } 41 | #endif 42 | 43 | /* 44 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious 45 | * initialization constants. 46 | */ 47 | MD5::MD5() 48 | { 49 | ctx->buf[0] = 0x67452301; 50 | ctx->buf[1] = 0xefcdab89; 51 | ctx->buf[2] = 0x98badcfe; 52 | ctx->buf[3] = 0x10325476; 53 | 54 | ctx->bits[0] = 0; 55 | ctx->bits[1] = 0; 56 | } 57 | 58 | /* 59 | * Update context to reflect the concatenation of another buffer full 60 | * of bytes. 61 | */ 62 | void MD5::update(const unsigned char *buf, unsigned len) 63 | { 64 | uint32 t; 65 | 66 | /* Update bitcount */ 67 | 68 | t = ctx->bits[0]; 69 | if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) 70 | ctx->bits[1]++; /* Carry from low to high */ 71 | ctx->bits[1] += len >> 29; 72 | 73 | t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ 74 | 75 | /* Handle any leading odd-sized chunks */ 76 | 77 | if (t) { 78 | unsigned char *p = (unsigned char *) ctx->in + t; 79 | 80 | t = 64 - t; 81 | if (len < t) { 82 | memcpy(p, buf, len); 83 | return; 84 | } 85 | memcpy(p, buf, t); 86 | byteReverse(ctx->in, 16); 87 | transform(ctx->buf, (uint32 *) ctx->in); 88 | buf += t; 89 | len -= t; 90 | } 91 | /* Process data in 64-byte chunks */ 92 | 93 | while (len >= 64) { 94 | memcpy(ctx->in, buf, 64); 95 | byteReverse(ctx->in, 16); 96 | transform(ctx->buf, (uint32 *) ctx->in); 97 | buf += 64; 98 | len -= 64; 99 | } 100 | 101 | /* Handle any remaining bytes of data. */ 102 | 103 | memcpy(ctx->in, buf, len); 104 | } 105 | 106 | /* 107 | * Final wrapup - pad to 64-byte boundary with the bit pattern 108 | * 1 0* (64-bit count of bits processed, MSB-first) 109 | */ 110 | void MD5::final(unsigned char digest[16]) 111 | { 112 | unsigned count; 113 | unsigned char *p; 114 | 115 | /* Compute number of bytes mod 64 */ 116 | count = (ctx->bits[0] >> 3) & 0x3F; 117 | 118 | /* Set the first char of padding to 0x80. This is safe since there is 119 | always at least one byte free */ 120 | p = ctx->in + count; 121 | *p++ = 0x80; 122 | 123 | /* Bytes of padding needed to make 64 bytes */ 124 | count = 64 - 1 - count; 125 | 126 | /* Pad out to 56 mod 64 */ 127 | if (count < 8) { 128 | /* Two lots of padding: Pad the first block to 64 bytes */ 129 | memset(p, 0, count); 130 | byteReverse(ctx->in, 16); 131 | transform(ctx->buf, (uint32 *) ctx->in); 132 | 133 | /* Now fill the next block with 56 bytes */ 134 | memset(ctx->in, 0, 56); 135 | } else { 136 | /* Pad block to 56 bytes */ 137 | memset(p, 0, count - 8); 138 | } 139 | byteReverse(ctx->in, 14); 140 | 141 | /* Append length in bits and transform */ 142 | //((uint32 *) ctx->in)[14] = ctx->bits[0]; 143 | //((uint32 *) ctx->in)[15] = ctx->bits[1]; 144 | memcpy(&ctx->in[14 * sizeof(uint32)], &ctx->bits[0], sizeof(uint32) * 2); 145 | 146 | transform(ctx->buf, (uint32 *) ctx->in); 147 | byteReverse((unsigned char *) ctx->buf, 4); 148 | memcpy(digest, ctx->buf, 16); 149 | memset(ctx, 0, sizeof(MD5)); /* In case it's sensitive */ 150 | } 151 | 152 | 153 | /* The four core functions - F1 is optimized somewhat */ 154 | 155 | /* #define F1(x, y, z) (x & y | ~x & z) */ 156 | #define F1(x, y, z) (z ^ (x & (y ^ z))) 157 | #define F2(x, y, z) F1(z, x, y) 158 | #define F3(x, y, z) (x ^ y ^ z) 159 | #define F4(x, y, z) (y ^ (x | ~z)) 160 | 161 | /* This is the central step in the MD5 algorithm. */ 162 | #define MD5STEP(f, w, x, y, z, data, s) \ 163 | ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) 164 | 165 | /* 166 | * The core of the MD5 algorithm, this alters an existing MD5 hash to 167 | * reflect the addition of 16 longwords of new data. MD5Update blocks 168 | * the data and converts bytes into longwords for this routine. 169 | */ 170 | void MD5::transform(uint32 buf[4], uint32 in[16]) 171 | { 172 | register uint32 a, b, c, d; 173 | 174 | a = buf[0]; 175 | b = buf[1]; 176 | c = buf[2]; 177 | d = buf[3]; 178 | 179 | MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); 180 | MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); 181 | MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); 182 | MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); 183 | MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); 184 | MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); 185 | MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); 186 | MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); 187 | MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); 188 | MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); 189 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); 190 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); 191 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); 192 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); 193 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); 194 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); 195 | 196 | MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); 197 | MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); 198 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); 199 | MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); 200 | MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); 201 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); 202 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); 203 | MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); 204 | MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); 205 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); 206 | MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); 207 | MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); 208 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); 209 | MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); 210 | MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); 211 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); 212 | 213 | MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); 214 | MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); 215 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); 216 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); 217 | MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); 218 | MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); 219 | MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); 220 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); 221 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); 222 | MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); 223 | MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); 224 | MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); 225 | MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); 226 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); 227 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); 228 | MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); 229 | 230 | MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); 231 | MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); 232 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); 233 | MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); 234 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); 235 | MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); 236 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); 237 | MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); 238 | MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); 239 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); 240 | MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); 241 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); 242 | MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); 243 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); 244 | MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); 245 | MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); 246 | 247 | buf[0] += a; 248 | buf[1] += b; 249 | buf[2] += c; 250 | buf[3] += d; 251 | } 252 | -------------------------------------------------------------------------------- /src/libmare/Tools/File.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #ifdef _WIN32 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #endif 13 | 14 | #include "Assert.h" 15 | #include "File.h" 16 | #include "String.h" 17 | 18 | File::File() 19 | { 20 | #ifdef _WIN32 21 | ASSERT(sizeof(void*) >= sizeof(HANDLE)); 22 | fp = INVALID_HANDLE_VALUE; 23 | #else 24 | fp = 0; 25 | #endif 26 | } 27 | 28 | File::~File() 29 | { 30 | close(); 31 | } 32 | 33 | void File::close() 34 | { 35 | #ifdef _WIN32 36 | if(fp != INVALID_HANDLE_VALUE) 37 | { 38 | CloseHandle((HANDLE)fp); 39 | fp = INVALID_HANDLE_VALUE; 40 | } 41 | #else 42 | if(fp) 43 | { 44 | fclose((FILE*)fp); 45 | fp = 0; 46 | } 47 | #endif 48 | } 49 | 50 | bool File::unlink(const String& file) 51 | { 52 | #ifdef _WIN32 53 | if(!DeleteFile(file.getData())) 54 | return false; 55 | #else 56 | if(::unlink(file.getData()) != 0) 57 | return false; 58 | #endif 59 | return true; 60 | } 61 | 62 | bool File::open(const String& file, Flags flags) 63 | { 64 | #ifdef _WIN32 65 | if(fp != INVALID_HANDLE_VALUE) 66 | { 67 | SetLastError(ERROR_INVALID_HANDLE); 68 | return false; 69 | } 70 | DWORD desiredAccess = 0, creationDisposition = 0; 71 | if(flags & writeFlag) 72 | { 73 | desiredAccess |= GENERIC_WRITE; 74 | creationDisposition |= CREATE_ALWAYS; 75 | } 76 | if(flags & readFlag) 77 | { 78 | desiredAccess |= GENERIC_READ; 79 | if(!(flags & writeFlag)) 80 | creationDisposition |= OPEN_EXISTING; 81 | } 82 | fp = CreateFileA(file.getData(), desiredAccess, FILE_SHARE_READ, NULL, creationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); 83 | if (fp == INVALID_HANDLE_VALUE) 84 | { 85 | if (GetLastError() == ERROR_PATH_NOT_FOUND) // path too long? try unicode stuff 86 | { 87 | String absFile = file; 88 | if (!File::isPathAbsolute(file)) 89 | { 90 | char curDir[MAX_PATH];; 91 | DWORD curDirLen; 92 | if ((curDirLen = GetCurrentDirectoryA(MAX_PATH, curDir)) == 0) 93 | { 94 | SetLastError(ERROR_PATH_NOT_FOUND); 95 | return false; 96 | } 97 | absFile.clear(); 98 | absFile.setCapacity(curDirLen + 1 + file.getLength()); 99 | absFile.append(curDir, curDirLen); 100 | absFile.append('\\'); 101 | absFile.append(file); 102 | absFile = File::simplifyPath(absFile); 103 | } 104 | 105 | int wFileLen = MultiByteToWideChar(CP_ACP, 0, absFile.getData(), static_cast(absFile.getLength()), 0, 0); 106 | int wlen = 4 + wFileLen + 1; 107 | WCHAR* path = (WCHAR*)alloca(wlen * sizeof(WCHAR)); 108 | memcpy(path, L"\\\\?\\", 4 * sizeof(WCHAR)); 109 | MultiByteToWideChar(CP_ACP, 0, absFile.getData(), static_cast(absFile.getLength()), path + 4, wFileLen + 1); 110 | path[4 + wFileLen] = L'\0'; 111 | for (WCHAR* p = path + 4; *p; ++p) 112 | if (*p == L'/') 113 | *p = L'\\'; 114 | fp = CreateFileW(path, desiredAccess, FILE_SHARE_READ, NULL, creationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); 115 | if (fp == INVALID_HANDLE_VALUE) 116 | return false; 117 | } 118 | else 119 | return false; 120 | } 121 | #else 122 | if(fp) 123 | return false; 124 | const char* mode = (flags & (writeFlag | readFlag)) == (writeFlag | readFlag) ? "w+" : (flags & writeFlag ? "w" : "r"); 125 | fp = fopen(file.getData(), mode); 126 | if(!fp) 127 | return false; 128 | #endif 129 | 130 | return true; 131 | } 132 | 133 | size_t File::read(char* buffer, size_t len) 134 | { 135 | #ifdef _WIN32 136 | DWORD i; 137 | if(!ReadFile((HANDLE)fp, buffer, static_cast(len), &i, NULL)) 138 | return 0; 139 | return i; 140 | #else 141 | size_t i = fread(buffer, 1, len, (FILE*)fp); 142 | if(i == 0) 143 | return 0; 144 | return i; 145 | #endif 146 | } 147 | 148 | size_t File::write(const char* buffer, size_t len) 149 | { 150 | #ifdef _WIN32 151 | DWORD i; 152 | if(!WriteFile((HANDLE)fp, buffer, static_cast(len), &i, NULL)) 153 | return 0; 154 | return i; 155 | #else 156 | size_t i = fwrite(buffer, 1, len, (FILE*)fp); 157 | return i; 158 | #endif 159 | } 160 | 161 | bool File::write(const String& data) 162 | { 163 | return write(data.getData(), data.getLength()) == data.getLength(); 164 | } 165 | 166 | String File::getDirname(const String& file) 167 | { 168 | const char* start = file.getData(); 169 | const char* pos = &start[file.getLength() - 1]; 170 | for(; pos >= start; --pos) 171 | if(*pos == '\\' || *pos == '/') 172 | return file.substr(0, pos - start); 173 | return String("."); 174 | } 175 | 176 | String File::getBasename(const String& file) 177 | { 178 | const char* start = file.getData(); 179 | const char* pos = &start[file.getLength() - 1]; 180 | for(; pos >= start; --pos) 181 | if(*pos == '\\' || *pos == '/') 182 | return file.substr(pos - start + 1); 183 | return file; 184 | } 185 | 186 | String File::getExtension(const String& file) 187 | { 188 | const char* start = file.getData(); 189 | const char* pos = &start[file.getLength() - 1]; 190 | for(; pos >= start; --pos) 191 | if(*pos == '.') 192 | return file.substr(pos - start + 1); 193 | else if(*pos == '\\' || *pos == '/') 194 | return String(); 195 | return String(); 196 | } 197 | 198 | String File::getWithoutExtension(const String& file) 199 | { 200 | const char* start = file.getData(); 201 | const char* pos = &start[file.getLength() - 1]; 202 | for(; pos >= start; --pos) 203 | if(*pos == '.') 204 | return file.substr(0, pos - start); 205 | else if(*pos == '\\' || *pos == '/') 206 | return file; 207 | return file; 208 | } 209 | 210 | String File::simplifyPath(const String& path) 211 | { 212 | String result(path.getLength()); 213 | const char* data = path.getData(); 214 | const char* start = data; 215 | const char* end; 216 | String chunck; 217 | bool startsWithSlash = *data == '/' || *data == '\\'; 218 | for(;;) 219 | { 220 | while(*start && (*start == '/' || *start == '\\')) 221 | ++start; 222 | end = start; 223 | while(*end && *end != '/' && *end != '\\') 224 | ++end; 225 | 226 | if(end == start) 227 | break; 228 | 229 | chunck = path.substr(start - data, end - start); 230 | if(chunck == ".." && !result.isEmpty()) 231 | { 232 | const char* data = result.getData(); 233 | const char* pos = data + result.getLength() - 1; 234 | for(;; --pos) 235 | if(pos < data || *pos == '/' || *pos == '\\') 236 | { 237 | if(strcmp(pos + 1, "..") != 0) 238 | { 239 | if(pos < data) 240 | result.setLength(0); 241 | else 242 | result.setLength(pos - data); 243 | goto cont; 244 | } 245 | break; 246 | } 247 | } 248 | else if(chunck == ".") 249 | goto cont; 250 | 251 | if(!result.isEmpty() || startsWithSlash) 252 | result.append('/'); 253 | result.append(chunck); 254 | 255 | cont: 256 | if(!*end) 257 | break; 258 | start = end + 1; 259 | } 260 | return result; 261 | } 262 | 263 | String File::relativePath(const String& from, const String& to) 264 | { 265 | String simFrom = simplifyPath(from); 266 | String simTo = simplifyPath(to); 267 | if(simFrom == simTo) 268 | return String("."); 269 | simFrom.append('/'); 270 | if(strncmp(simTo.getData(), simFrom.getData(), simFrom.getLength()) == 0) 271 | return String(simTo.getData() + simFrom.getLength(), simTo.getLength() - simFrom.getLength()); 272 | String result("../"); 273 | while(simFrom.getLength() > 0) 274 | { 275 | simFrom.setLength(simFrom.getLength() - 1); 276 | const char* newEnd = strrchr(simFrom.getData(), '/'); 277 | if(!newEnd) 278 | break; 279 | simFrom.setLength((newEnd - simFrom.getData()) + 1); 280 | if(strncmp(simTo.getData(), simFrom.getData(), simFrom.getLength()) == 0) 281 | { 282 | result.append(String(simTo.getData() + simFrom.getLength(), simTo.getLength() - simFrom.getLength())); 283 | return result; 284 | } 285 | result.append("../"); 286 | } 287 | return String(); 288 | } 289 | 290 | bool File::isPathAbsolute(const String& path) 291 | { 292 | const char* data = path.getData(); 293 | return *data == '/' || *data == '\\' || (path.getLength() > 2 && data[1] == ':' && (data[2] == '/' || data[2] == '\\')); 294 | } 295 | 296 | bool File::getWriteTime(const String& file, long long& writeTime) 297 | { 298 | #ifdef _WIN32 299 | WIN32_FIND_DATAA wfd; 300 | HANDLE hFind = FindFirstFileA(file.getData(), &wfd); 301 | if(hFind == INVALID_HANDLE_VALUE) 302 | return false; 303 | ASSERT(sizeof(DWORD) == 4); 304 | writeTime = ((long long)wfd.ftLastWriteTime.dwHighDateTime) << 32LL | ((long long)wfd.ftLastWriteTime.dwLowDateTime); 305 | FindClose(hFind); 306 | return true; 307 | #else 308 | struct stat buf; 309 | if(stat(file.getData(), &buf) != 0) 310 | return false; 311 | writeTime = ((long long)buf.st_mtim.tv_sec) * 1000000000LL + ((long long)buf.st_mtim.tv_nsec); 312 | return true; 313 | #endif 314 | } 315 | 316 | bool File::exists(const String& file) 317 | { 318 | #ifdef _WIN32 319 | WIN32_FIND_DATAA wfd; 320 | HANDLE hFind = FindFirstFileA(file.getData(), &wfd); 321 | if(hFind == INVALID_HANDLE_VALUE) 322 | return false; 323 | //bool isDir = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY; 324 | FindClose(hFind); 325 | //return isDir; 326 | return true; 327 | #else 328 | struct stat buf; 329 | if(lstat(file.getData(), &buf) != 0) 330 | return false; 331 | return true; 332 | #endif 333 | } 334 | -------------------------------------------------------------------------------- /src/mare/JsonDb.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Tools/Assert.h" 3 | #include "Tools/Directory.h" 4 | #include "Tools/File.h" 5 | #include "Tools/Process.h" 6 | #include "Engine.h" 7 | 8 | #include "JsonDb.h" 9 | 10 | bool JsonDb::generate(const Map& userArgs) 11 | { 12 | // add default rules and stuff 13 | engine.addDefaultKey("cCompiler", "gcc"); 14 | engine.addDefaultKey("cppCompiler", "g++"); 15 | engine.addDefaultKey("configurations", "Debug Release"); 16 | engine.addDefaultKey("targets"); // an empty target list exists per default 17 | engine.addDefaultKey("buildDir", "$(configuration)"); 18 | engine.addDefaultKey("outputDir", "$(buildDir)"); 19 | engine.addDefaultKey("cFlags", "-Wall $(if $(Debug),-g,-Os -fomit-frame-pointer)"); 20 | engine.addDefaultKey("cppFlags", "-Wall $(if $(Debug),-g,-Os -fomit-frame-pointer)"); 21 | engine.addDefaultKey("linkFlags", "$(if $(Debug),,-s)"); 22 | { 23 | Map cppApplication; 24 | cppApplication.append("input", "$(addprefix $(buildDir)/,$(addsuffix .o,$(basename $(subst ../,,$(filter %.c%,$(files))))))"); 25 | cppApplication.append("output", "$(outputDir)/$(target)$(if $(Win32),.exe)"); 26 | cppApplication.append("command", "$(linker) -o $(output) $(input) $(linkFlags) $(LDFLAGS) $(patsubst %,-L%,$(libPaths)) $(patsubst %,-l%,$(libs))"); 27 | cppApplication.append("message", "-> $(output)"); 28 | cppApplication.append("linker", "$(if $(linker),$(linker),$(cppCompiler))"); 29 | engine.addDefaultKey("cppApplication", cppApplication); 30 | } 31 | { 32 | Map cppDynamicLibrary; 33 | cppDynamicLibrary.append("input", "$(addprefix $(buildDir)/,$(addsuffix .o,$(basename $(subst ../,,$(filter %.c%,$(files))))))"); 34 | cppDynamicLibrary.append("output", "$(outputDir)/$(if $(Win32),,lib)$(patsubst lib%,%,$(target))$(if $(Win32),.dll,.so)"); 35 | cppDynamicLibrary.append("__soFlags", "$(if $(Win32),,-fpic)"); 36 | cppDynamicLibrary.append("command", "$(linker) -shared $(__soFlags) -o $(output) $(input) $(linkFlags) $(LDFLAGS) $(patsubst %,-L%,$(libPaths)) $(patsubst %,-l%,$(libs))"); 37 | cppDynamicLibrary.append("message", "-> $(output)"); 38 | cppDynamicLibrary.append("linker", "$(if $(linker),$(linker),$(cppCompiler))"); 39 | engine.addDefaultKey("cppDynamicLibrary", cppDynamicLibrary); 40 | } 41 | { 42 | Map cppStaticLibrary; 43 | cppStaticLibrary.append("input", "$(addprefix $(buildDir)/,$(addsuffix .o,$(basename $(subst ../,,$(filter %.c%,$(files))))))"); 44 | cppStaticLibrary.append("output", "$(outputDir)/$(if $(Win32),,lib)$(patsubst lib%,%,$(target))$(if $(Win32),.lib,.a)"); 45 | cppStaticLibrary.append("command", "$(linker) rcs $(output) $(input)"); 46 | cppStaticLibrary.append("message", "-> $(output)"); 47 | cppStaticLibrary.append("linker", "$(if $(linker),$(linker),ar)"); 48 | engine.addDefaultKey("cppStaticLibrary", cppStaticLibrary); 49 | } 50 | { 51 | Map cApplication; 52 | cApplication.append("input", "$(addprefix $(buildDir)/,$(addsuffix .o,$(basename $(subst ../,,$(filter %.c%,$(files))))))"); 53 | cApplication.append("output", "$(outputDir)/$(target)$(if $(Win32),.exe)"); 54 | cApplication.append("command", "$(linker) -o $(output) $(input) $(linkFlags) $(LDFLAGS) $(patsubst %,-L%,$(libPaths)) $(patsubst %,-l%,$(libs))"); 55 | cApplication.append("message", "-> $(output)"); 56 | cApplication.append("linker", "$(if $(linker),$(linker),$(cCompiler))"); 57 | engine.addDefaultKey("cApplication", cApplication); 58 | } 59 | { 60 | Map cDynamicLibrary; 61 | cDynamicLibrary.append("input", "$(addprefix $(buildDir)/,$(addsuffix .o,$(basename $(subst ../,,$(filter %.c%,$(files))))))"); 62 | cDynamicLibrary.append("output", "$(outputDir)/$(if $(Win32),,lib)$(patsubst lib%,%,$(target))$(if $(Win32),.dll,.so)"); 63 | cDynamicLibrary.append("__soFlags", "$(if $(Win32),,-fpic)"); 64 | cDynamicLibrary.append("command", "$(linker) -shared $(__soFlags) -o $(output) $(input) $(linkFlags) $(LDFLAGS) $(patsubst %,-L%,$(libPaths)) $(patsubst %,-l%,$(libs))"); 65 | cDynamicLibrary.append("message", "-> $(output)"); 66 | cDynamicLibrary.append("linker", "$(if $(linker),$(linker),$(cCompiler))"); 67 | engine.addDefaultKey("cDynamicLibrary", cDynamicLibrary); 68 | } 69 | { 70 | Map cStaticLibrary; 71 | cStaticLibrary.append("input", "$(addprefix $(buildDir)/,$(addsuffix .o,$(basename $(subst ../,,$(filter %.c%,$(files))))))"); 72 | cStaticLibrary.append("output", "$(outputDir)/$(if $(Win32),,lib)$(patsubst lib%,%,$(target))$(if $(Win32),.lib,.a)"); 73 | cStaticLibrary.append("command", "$(linker) rcs $(output) $(input)"); 74 | cStaticLibrary.append("message", "-> $(output)"); 75 | cStaticLibrary.append("linker", "$(if $(linker),$(linker),ar)"); 76 | engine.addDefaultKey("cStaticLibrary", cStaticLibrary); 77 | } 78 | { 79 | Map cppSource; 80 | cppSource.append("__ofile", "$(buildDir)/$(basename $(subst ../,,$(file))).o"); 81 | cppSource.append("__dfile", "$(patsubst %.o,%.d,$(__ofile))"); 82 | cppSource.append("input", "$(file) $(filter-out %.o: \\,$(readfile $(__dfile)))"); 83 | cppSource.append("output", "$(__ofile) $(__dfile)"); 84 | cppSource.append("command", "$(cppCompiler) -MMD $(__soFlags) -o $(__ofile) -c $(file) $(cppFlags) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(patsubst %,-D%,$(defines)) $(patsubst %,-I%,$(includePaths))"); 85 | cppSource.append("message", "$(subst ./,,$(file))"); 86 | engine.addDefaultKey("cppSource", cppSource); 87 | } 88 | { 89 | Map cSource; 90 | cSource.append("__ofile", "$(buildDir)/$(basename $(subst ../,,$(file))).o"); 91 | cSource.append("__dfile", "$(patsubst %.o,%.d,$(__ofile))"); 92 | cSource.append("input", "$(file) $(filter-out %.o: \\,$(readfile $(__dfile)))"); 93 | cSource.append("output", "$(__ofile) $(__dfile)"); 94 | cSource.append("command", "$(cCompiler) -MMD $(__soFlags) -o $(__ofile) -c $(file) $(cFlags) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(patsubst %,-D%,$(defines)) $(patsubst %,-I%,$(includePaths))"); 95 | cSource.append("message", "$(subst ./,,$(file))"); 96 | engine.addDefaultKey("cSource", cSource); 97 | } 98 | #if defined(_WIN32) || defined(__CYGWIN__) 99 | String platform("Win32"); 100 | #elif defined(__linux) 101 | String platform("Linux"); 102 | #elif defined(__APPLE__) && defined(__MACH__) 103 | String platform("MacOSX"); 104 | #else 105 | String platform("unknown"); 106 | // add your os :) 107 | // http://predef.sourceforge.net/preos.html 108 | #endif 109 | engine.addDefaultKey("host", platform); // the platform on which the compiler is run 110 | engine.addDefaultKey("platforms", platform); // the target platform of the compiler 111 | 112 | String architecture = Process::getArchitecture(); 113 | engine.addDefaultKey("architecture", architecture); 114 | engine.addDefaultKey("arch", architecture); 115 | 116 | // add user arguments 117 | for(const Map::Node* i = userArgs.getFirst(); i; i = i->getNext()) 118 | engine.addCommandLineKey(i->key, i->data); 119 | 120 | engine.enterRootKey(); 121 | List allPlatforms, allConfigurations, allTargets; 122 | engine.getKeys("platforms", allPlatforms); 123 | engine.getKeys("configurations", allConfigurations); 124 | engine.getKeys("targets", allTargets); 125 | engine.leaveKey(); 126 | 127 | List files, inputs, outputs, command; 128 | 129 | File jsonCompilationDatabase; 130 | 131 | for(const List::Node* i = allPlatforms.getFirst(); i; i = i->getNext()) 132 | { 133 | const String& platform = i->data; 134 | for(const List::Node* i = allConfigurations.getFirst(); i; i = i->getNext()) 135 | { 136 | const String& configuration = i->data; 137 | for(const List::Node* i = allTargets.getFirst(); i; i = i->getNext()) 138 | { 139 | const String& target = i->data; 140 | 141 | engine.enterUnnamedKey(); 142 | engine.addDefaultKey("platform", platform); 143 | engine.addDefaultKey(platform, platform); 144 | engine.addDefaultKey("configuration", configuration); 145 | engine.addDefaultKey(configuration, configuration); 146 | engine.addDefaultKey("target", target); 147 | 148 | engine.enterRootKey(); 149 | VERIFY(engine.enterKey("targets")); 150 | if(!engine.enterKey(target)) 151 | { 152 | engine.error(String().format(256, "cannot find target \"%s\"", i->data.getData())); 153 | return false; 154 | } 155 | 156 | String mareDir = engine.getMareDir(); 157 | engine.addDefaultKey("mareDir", mareDir); 158 | 159 | if(engine.enterKey("files")) 160 | { 161 | String outputDir = engine.getFirstKey("outputDir", true); 162 | if(!Directory::exists(outputDir) && !Directory::create(outputDir)) 163 | return false; 164 | 165 | VERIFY(jsonCompilationDatabase.open(outputDir + "/compile_commands.json", File::writeFlag)); 166 | jsonCompilationDatabase.write("[", 1); 167 | 168 | bool first = true; 169 | 170 | files.clear(); 171 | engine.getKeys(files); 172 | for(List::Node* i = files.getFirst(); i; i = i->getNext()) 173 | { 174 | inputs.clear(); 175 | outputs.clear(); 176 | command.clear(); 177 | engine.enterUnnamedKey(); 178 | engine.addDefaultKey("file", i->data); 179 | VERIFY(engine.enterKey(i->data)); 180 | engine.getKeys("input", inputs, false); 181 | engine.getKeys("output", outputs, false); 182 | engine.getText("command", command, false); 183 | 184 | if(!inputs.isEmpty() && !outputs.isEmpty() && command.getSize() == 1) 185 | { 186 | if(!first) 187 | jsonCompilationDatabase.write(",", 1); 188 | else 189 | first = false; 190 | jsonCompilationDatabase.write("\n{\n \"directory\": \"", 19); 191 | writeJsonEscaped(jsonCompilationDatabase, Directory::getCurrent() + "/" + mareDir); 192 | jsonCompilationDatabase.write("\",\n \"command\": \"", 17); 193 | writeJsonEscaped(jsonCompilationDatabase, command.getFirst()->data); 194 | jsonCompilationDatabase.write("\",\n \"file\": \"", 14); 195 | writeJsonEscaped(jsonCompilationDatabase, inputs.getFirst()->data); 196 | jsonCompilationDatabase.write("\"\n}", 3); 197 | } 198 | 199 | engine.leaveKey(); 200 | engine.leaveKey(); 201 | } 202 | engine.leaveKey(); 203 | 204 | jsonCompilationDatabase.write("\n]\n", 3); 205 | jsonCompilationDatabase.close(); 206 | } 207 | 208 | engine.leaveKey(); 209 | engine.leaveKey(); 210 | engine.leaveKey(); 211 | engine.leaveKey(); 212 | } 213 | } 214 | } 215 | 216 | return true; 217 | } 218 | 219 | void JsonDb::writeJsonEscaped(File& f, const String& string) 220 | { 221 | const char* data = string.getData(); 222 | for(size_t i = 0; i < string.getLength(); i++) 223 | { 224 | if(data[i] == '\\') 225 | f.write("\\\\", 2); 226 | else if(data[i] == '\"') 227 | f.write("\\\"", 2); 228 | else 229 | f.write(data + i, 1); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/libmare/Tools/Directory.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Directory.cpp 3 | * Implementation of a class for accessing directories. 4 | * @author Colin Graf 5 | */ 6 | 7 | #include 8 | #ifdef _WIN32 9 | #include 10 | #else 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #endif 18 | 19 | #include "Assert.h" 20 | #include "Directory.h" 21 | #include "List.h" 22 | #include "Map.h" 23 | #include "File.h" 24 | 25 | Directory::Directory() 26 | { 27 | #ifdef _WIN32 28 | findFile = INVALID_HANDLE_VALUE; 29 | ASSERT(sizeof(ffd) >= sizeof(WIN32_FIND_DATA)); 30 | #else 31 | dp = 0; 32 | #endif 33 | } 34 | 35 | Directory::~Directory() 36 | { 37 | #ifdef _WIN32 38 | if(findFile != INVALID_HANDLE_VALUE) 39 | FindClose((HANDLE)findFile); 40 | #else 41 | if(dp) 42 | closedir((DIR*)dp); 43 | #endif 44 | } 45 | 46 | bool Directory::remove(const String& dir) 47 | { 48 | String path = dir; 49 | while(path != ".") 50 | { 51 | #ifdef _WIN32 52 | if(!RemoveDirectory(path.getData())) 53 | return false; 54 | #else 55 | if(rmdir(path.getData()) != 0) 56 | return false; 57 | #endif 58 | path = File::getDirname(path); 59 | } 60 | return true; 61 | } 62 | 63 | bool Directory::open(const String& dirpath, const String& pattern, bool dirsOnly) 64 | { 65 | #ifdef _WIN32 66 | if(findFile != INVALID_HANDLE_VALUE) 67 | { 68 | SetLastError(ERROR_INVALID_HANDLE); 69 | return false; 70 | } 71 | 72 | this->dirsOnly = dirsOnly; 73 | this->dirpath = dirpath; 74 | const char* patExt = strrchr(pattern.getData(), '.'); 75 | this->patternExtension = (patExt && !strpbrk(patExt + 1, "*?")) ? String(patExt + 1, -1) : String(); 76 | 77 | String searchPath = dirpath; 78 | searchPath.setCapacity(dirpath.getLength() + 1 + pattern.getLength()); 79 | if(!dirpath.isEmpty()) 80 | searchPath.append('/'); 81 | searchPath.append(pattern); 82 | 83 | findFile = FindFirstFileEx(searchPath.getData(), 84 | #if _WIN32_WINNT > 0x0600 85 | FindExInfoBasic, 86 | #else 87 | FindExInfoStandard, 88 | #endif 89 | (LPWIN32_FIND_DATA)ffd, dirsOnly ? FindExSearchLimitToDirectories : FindExSearchNameMatch, NULL, 0); 90 | if(findFile == INVALID_HANDLE_VALUE) 91 | return false; 92 | bufferedEntry = true; 93 | return true; 94 | #else 95 | if(dp) 96 | { 97 | errno = EINVAL; 98 | return false; 99 | } 100 | 101 | this->dirsOnly = dirsOnly; 102 | this->dirpath = dirpath; 103 | this->pattern = pattern; 104 | 105 | dp = opendir(dirpath.isEmpty() ? "." : dirpath.getData()); 106 | return dp != 0; 107 | #endif 108 | } 109 | 110 | bool Directory::read(String& name, bool& isDir) 111 | { 112 | #ifdef _WIN32 113 | if(!findFile) 114 | { 115 | SetLastError(ERROR_INVALID_HANDLE); 116 | return false; 117 | } 118 | for(;;) 119 | { 120 | if(bufferedEntry) 121 | bufferedEntry = false; 122 | else if(!FindNextFile((HANDLE)findFile, (LPWIN32_FIND_DATA)ffd)) 123 | { 124 | DWORD lastError = GetLastError(); 125 | FindClose((HANDLE)findFile); 126 | findFile = INVALID_HANDLE_VALUE; 127 | SetLastError(lastError); 128 | return false; 129 | } 130 | isDir = (((LPWIN32_FIND_DATA)ffd)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; 131 | if(dirsOnly && !isDir) 132 | continue; 133 | const char* str = ((LPWIN32_FIND_DATA)ffd)->cFileName; 134 | if(isDir && *str == '.' && (str[1] == '\0' || (str[1] == '.' && str[2] == '\0'))) 135 | continue; 136 | 137 | if(!patternExtension.isEmpty()) 138 | { 139 | const char* patExt = strrchr(str, '.'); 140 | if(!patExt || _stricmp(patternExtension.getData(), patExt + 1) != 0) 141 | continue; 142 | } 143 | 144 | name = String(str, -1); 145 | return true; 146 | } 147 | #else 148 | if(!dp) 149 | { 150 | errno = EINVAL; 151 | return false; 152 | } 153 | 154 | for(;;) 155 | { 156 | struct dirent* dent = readdir((DIR*)dp); 157 | if(!dent) 158 | { 159 | int lastErrno = errno; 160 | closedir((DIR*)dp); 161 | dp = 0; 162 | errno = lastErrno; 163 | return false; 164 | } 165 | const char* const str = dent->d_name; 166 | if(fnmatch(pattern.getData(), str, 0) == 0) 167 | { 168 | name = String(str, -1); 169 | isDir = false; 170 | if(dent->d_type == DT_DIR) 171 | isDir = true; 172 | else if(dent->d_type == DT_LNK || dent->d_type == DT_UNKNOWN) 173 | { 174 | String path = dirpath; 175 | if(!dirpath.isEmpty()) 176 | path.append('/'); 177 | path.append(name); 178 | struct stat buff; 179 | if(stat(path.getData(), &buff) == 0) 180 | if(S_ISDIR(buff.st_mode)) 181 | isDir = true; 182 | } 183 | if(dirsOnly && !isDir) 184 | continue; 185 | if(isDir && *str == '.' && (str[1] == '\0' || (str[1] == '.' && str[2] == '\0'))) 186 | continue; 187 | return true; 188 | } 189 | } 190 | return false; // unreachable 191 | #endif 192 | } 193 | 194 | void Directory::findFiles(const String& pattern, List& files) 195 | { 196 | // replace ** with * / ** and split in chunks 197 | List chunks; 198 | int start = 0, i = 0; 199 | for(const char* str = pattern.getData(), * end = str + pattern.getLength(); str <= end; ++str, ++i) 200 | switch(*str) 201 | { 202 | case '/': 203 | case '\\': 204 | case '\0': 205 | int len = i - start; 206 | if(len > 0) 207 | chunks.append(pattern.substr(start, len)); 208 | start = i + 1; 209 | break; 210 | } 211 | 212 | // 213 | struct FindFiles 214 | { 215 | List* files; 216 | bool dirsOnly; 217 | 218 | void handlePath(const String& path, const String& pattern, const List::Node* nextChunk) 219 | { 220 | const char* useWildcards = strpbrk(pattern.getData(), "*?"); 221 | const char* doubleStar = useWildcards ? strstr(useWildcards, "**") : 0; 222 | if(doubleStar) 223 | { 224 | /* 225 | if a**b / c 226 | use a*b / c 227 | use a* / **b / c 228 | if a** / c 229 | use a* / c //? 230 | use a* / ** / c 231 | if **b / c 232 | use *b / c 233 | use * / **b / c 234 | if ** / 235 | use * 236 | use * / ** 237 | if ** / c 238 | use * / c //? 239 | //use c //? 240 | use * / ** / c 241 | */ 242 | 243 | const List::Node* nextNextChunk = 0; 244 | String nextPattern; 245 | if(nextChunk) 246 | { 247 | nextNextChunk = nextChunk->getNext(); 248 | nextPattern = nextChunk->data; 249 | } 250 | 251 | if(doubleStar != pattern.getData()) 252 | { 253 | if (doubleStar != pattern.getData() + pattern.getLength() - 2) // pattern == a**b 254 | { 255 | String subPattern(pattern.getLength()); 256 | subPattern.append(pattern.getData(), doubleStar - pattern.getData()); // = a 257 | subPattern.append(doubleStar + 1, pattern.getLength() - (doubleStar + 1 - pattern.getData())); // += *b 258 | handlePath2(path, subPattern, nextPattern, nextNextChunk); 259 | subPattern.clear(); 260 | subPattern.append(pattern.getData(), doubleStar + 1 - pattern.getData()); 261 | String nextPattern2(doubleStar, pattern.getLength() - (doubleStar - pattern.getData())); 262 | handlePath2(path, subPattern, nextPattern2, nextChunk); 263 | } 264 | else // pattern == a** 265 | { 266 | String subPattern(pattern.getData(), pattern.getLength() - 1); 267 | handlePath2(path, subPattern, nextPattern, nextNextChunk); 268 | handlePath2(path, subPattern, "**", nextChunk); 269 | } 270 | } 271 | else // pattern == **b || pattern == ** 272 | { 273 | if(pattern.getLength() != 2) // pattern == **b 274 | { 275 | handlePath2(path, String(doubleStar + 1, pattern.getLength() - 1), nextPattern, nextNextChunk); 276 | handlePath2(path, "*", pattern, nextChunk); 277 | } 278 | else // pattern == ** 279 | { 280 | handlePath2(path, "*", nextPattern, nextNextChunk); 281 | handlePath2(path, "*", pattern, nextChunk); 282 | } 283 | } 284 | } 285 | else if(useWildcards || nextChunk == 0) 286 | { 287 | const List::Node* nextNextChunk = 0; 288 | String nextPattern; 289 | if(nextChunk) 290 | { 291 | nextNextChunk = nextChunk->getNext(); 292 | nextPattern = nextChunk->data; 293 | } 294 | handlePath2(path, pattern, nextPattern, nextNextChunk); 295 | } 296 | else // not a pattern 297 | handleSubPath(path, pattern, true, nextChunk->data, nextChunk->getNext()); 298 | } 299 | 300 | void handlePath2(const String& path, const String& pattern, const String& nextPattern, const List::Node* nextNextChunk) 301 | { 302 | Directory dir; String name; bool isDir; 303 | if(dir.open(path, pattern, !nextPattern.isEmpty() || dirsOnly)) 304 | while(dir.read(name, isDir)) 305 | handleSubPath(path, name, isDir, nextPattern, nextNextChunk); 306 | } 307 | 308 | void handleSubPath(const String& path, const String& name, bool isDir, const String& nextPattern, const List::Node* nextNextChunk) 309 | { 310 | String subpath = path; 311 | subpath.setCapacity(path.getLength() + 2 + name.getLength()); 312 | if(!path.isEmpty()) 313 | subpath.append('/'); 314 | subpath.append(name); 315 | if(nextPattern.isEmpty()) 316 | { 317 | if(dirsOnly) 318 | { 319 | if(isDir) 320 | { 321 | subpath.append('/'); 322 | files->append(subpath); 323 | } 324 | } 325 | else 326 | files->append(subpath); 327 | } 328 | else if(isDir) 329 | handlePath(subpath, nextPattern, nextNextChunk); 330 | } 331 | }; 332 | 333 | if(!chunks.isEmpty()) 334 | { 335 | FindFiles ff; 336 | char lastChar = pattern.getData()[pattern.getLength() - 1]; 337 | ff.dirsOnly = lastChar == '/' || lastChar == '\\'; 338 | ff.files = &files; 339 | ff.handlePath(String(), chunks.getFirst()->data, chunks.getFirst()->getNext()); 340 | } 341 | } 342 | 343 | bool Directory::exists(const String& dir) 344 | { 345 | #ifdef _WIN32 346 | WIN32_FIND_DATAA wfd; 347 | HANDLE hFind = FindFirstFileA(dir.getData(), &wfd); 348 | if(hFind == INVALID_HANDLE_VALUE) 349 | return false; 350 | bool isDir = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; 351 | FindClose(hFind); 352 | return isDir; 353 | #else 354 | struct stat buf; 355 | if(stat(dir.getData(), &buf) != 0) 356 | return false; 357 | return S_ISDIR(buf.st_mode); 358 | #endif 359 | } 360 | 361 | bool Directory::create(const String& dir) 362 | { 363 | // TODO: set errno correctly 364 | 365 | static Map createdDirs; 366 | Map::Node* i = createdDirs.find(dir); 367 | if(i) 368 | return i->data; 369 | 370 | if(exists(dir)) 371 | { 372 | createdDirs.append(dir, true); 373 | return true; 374 | } 375 | 376 | const char* start = dir.getData(); 377 | const char* pos = &start[dir.getLength() - 1]; 378 | for(; pos >= start; --pos) 379 | if(*pos == '\\' || *pos == '/') 380 | { 381 | if(!create(dir.substr(0, pos - start))) 382 | { 383 | createdDirs.append(dir, false); 384 | return false; 385 | } 386 | break; 387 | } 388 | ++pos; 389 | bool result = false; 390 | if(*pos) 391 | { 392 | if(*pos == '.' && (pos[1] == '\0' || (pos[1] == '.' && pos[2] == '\0'))) 393 | result = true; 394 | else 395 | #ifdef _WIN32 396 | result = CreateDirectory(dir.getData(), NULL) == TRUE; 397 | #else 398 | result = mkdir(dir.getData(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0; 399 | #endif 400 | } 401 | createdDirs.append(dir, result); 402 | return result; 403 | } 404 | 405 | bool Directory::change(const String& dir) 406 | { 407 | #ifdef _WIN32 408 | return SetCurrentDirectory(dir.getData()) != FALSE; 409 | #else 410 | return chdir(dir.getData()) == 0; 411 | #endif 412 | } 413 | 414 | String Directory::getCurrent() 415 | { 416 | #ifdef _WIN32 417 | TCHAR buffer[MAX_PATH]; 418 | DWORD len = GetCurrentDirectory(MAX_PATH, buffer); 419 | return String(buffer, len); 420 | #else 421 | char buffer[PATH_MAX]; 422 | if(!getcwd(buffer, PATH_MAX)) 423 | return String(); 424 | return String(buffer, strlen(buffer)); 425 | #endif 426 | } 427 | -------------------------------------------------------------------------------- /src/mare/Main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Engine.h" 8 | #include "Tools/File.h" 9 | #include "Tools/Directory.h" 10 | #include "Tools/Error.h" 11 | #ifdef _WIN32 12 | #include "Tools/Win32/getopt.h" 13 | #else 14 | #include 15 | #endif 16 | 17 | #include "Mare.h" 18 | #include "Make.h" 19 | #include "Vcxproj.h" 20 | #include "Vcproj.h" 21 | #include "CodeLite.h" 22 | #include "CodeBlocks.h" 23 | #include "CMake.h" 24 | #include "NetBeans.h" 25 | #include "JsonDb.h" 26 | 27 | static const char* VERSION = "0.3"; 28 | static const char* COPYRIGHT = "Copyright (C) 2011-2013 Colin Graf"; 29 | 30 | static void errorHandler(void* userData, const String& file, int line, const String& message) 31 | { 32 | if(line < 0) 33 | fprintf(stderr, "%s: %s\n", (const char*)userData, message.getData()); 34 | else 35 | { 36 | if(line > 0) 37 | fprintf(stderr, "%s:%u: error: %s\n", file.getData(), line, message.getData()); 38 | else 39 | fprintf(stderr, "%s: %s\n", file.getData(), message.getData()); 40 | } 41 | } 42 | 43 | static void showVersion(bool andExit) 44 | { 45 | printf("mare %s, the lightweight build system\n", VERSION); 46 | puts(COPYRIGHT); 47 | //printf("Author(s): %s\n", AUTHORS); 48 | puts("This program comes with ABSOLUTELY NO WARRANTY."); 49 | puts("This is free software, and you are welcome to redistribute it under certain"); 50 | puts("conditions."); 51 | if(andExit) 52 | exit(EXIT_SUCCESS); 53 | } 54 | 55 | static void showUsage(const char* executable) 56 | { 57 | String basename = File::getBasename(String(executable, -1)); 58 | showVersion(false); 59 | puts(""); 60 | printf("Usage: %s [ -f ] [ ] [ config= ] [ ]\n", basename.getData()); 61 | puts(" [ platform= ] [ = ] [ clean | rebuild ]"); 62 | puts(""); 63 | puts("Options:"); 64 | puts(""); 65 | puts(" -f , --file="); 66 | puts(" Use as input marefile."); 67 | puts(" The default value of is \"Marefile\"."); 68 | puts(""); 69 | puts(" --vcxproj[=]"); 70 | puts(" Convert the marefile into a .sln and .vcxproj files for Visual Studio."); 71 | puts(" can be set to 2010, 2012, 2013, 2015, 2017 or 2019."); 72 | puts(""); 73 | puts(" --vcproj[=]"); 74 | puts(" Convert the marefile into a .sln and .vcproj files for Visual Studio."); 75 | puts(" can be set to 2008."); 76 | puts(""); 77 | puts(" --make"); 78 | puts(" Try to translate the marefile into a Makefile. (experimental)"); 79 | puts(""); 80 | puts(" --codelite"); 81 | puts(" Try to translate the marefile into a .workspace and .project files for"); 82 | puts(" CodeLite. (experimental)"); 83 | puts(""); 84 | puts(" --codeblocks"); 85 | puts(" Try to translate the marefile into a .workspace and .cbp files for"); 86 | puts(" Code::Blocks. (experimental)"); 87 | puts(""); 88 | puts(" --cmake"); 89 | puts(" Try to translate the marefile into a CMakeLists.txt files for cmake."); 90 | puts(" (experimental)"); 91 | puts(""); 92 | puts(" --netbeans"); 93 | puts(" Generate project files for NetBeans. (experimental)"); 94 | puts(""); 95 | puts(" --jsondb"); 96 | puts(" Generate JSON compilation database."); 97 | puts(""); 98 | puts(" config=, --config="); 99 | puts(" Build using configuration as declared in the marefile (Debug"); 100 | puts(" and Release by default). Multiple configurations can be used by adding"); 101 | puts(" config= multiple times."); 102 | puts(""); 103 | puts(" , target=, --target="); 104 | puts(" Build as declared in the marefile. Multiple targets can be"); 105 | puts(" used."); 106 | puts(""); 107 | puts(" platform=, --platform="); 108 | puts(" Build the target for platform . By default the native"); 109 | puts(" build platform of the mare executable or the native platform of a"); 110 | puts(" generator (e.g. \"Win32\" for vcxproj) will be used. Multiple platforms"); 111 | puts(" can be used by adding platform= multiple times. Available"); 112 | puts(" platforms can be declared in the mare."); 113 | puts(""); 114 | puts(" =, --[=]"); 115 | puts(" Set any variable to . This can be used to set"); 116 | puts(" various options with a meaning defined by the marefile."); 117 | puts(""); 118 | puts(" clean, --clean"); 119 | puts(" Delete all output files instead of building the selected target."); 120 | puts(""); 121 | puts(" rebuild, --rebuild"); 122 | puts(" Rebuild all output files of the selected target."); 123 | puts(""); 124 | puts(" -C , --directory="); 125 | puts(" Change working directory to before doing anything else."); 126 | puts(""); 127 | puts(" -d"); 128 | puts(" Print debugging information while processing normally."); 129 | puts(""); 130 | puts(" -j "); 131 | puts(" Use processes in parallel for building alle targets. The default"); 132 | puts(" value for is the number of processors on the host system."); 133 | puts(""); 134 | puts(" --ignore-dependencies"); 135 | puts(" Do not respect dependencies between build targets."); 136 | puts(""); 137 | puts(" -h, --help"); 138 | puts(" Display this help message or a help message declared in the marefile."); 139 | puts(""); 140 | puts(" -v, --version"); 141 | puts(" Display version information."); 142 | puts(""); 143 | exit(EXIT_SUCCESS); 144 | } 145 | 146 | static void showHelp(const char* executable) 147 | { 148 | fprintf(stderr, "Type '%s --help' for help\n", executable); 149 | exit(EXIT_FAILURE); 150 | } 151 | 152 | int main(int argc, char* argv[]) 153 | { 154 | Map userArgs; 155 | List inputPlatforms, inputConfigs, inputTargets; 156 | String inputFile("Marefile"), inputDir; 157 | bool showHelp = false; 158 | bool showDebug = false; 159 | bool clean = false; 160 | bool rebuild = false; 161 | bool ignoreDependencies = false; 162 | int jobs = 0; 163 | bool generateMake = false; 164 | int generateVcxproj = 0; 165 | int generateVcproj = 0; 166 | bool generateCodeLite = false; 167 | bool generateCodeBlocks = false; 168 | bool generateCMake = false; 169 | bool generateNetBeans = false; 170 | bool generateJsonDb = false; 171 | 172 | // parse args 173 | { 174 | int c, option_index; 175 | static struct option long_options[] = { 176 | {"file", required_argument , 0, 'f'}, 177 | {"help", no_argument , 0, 'h'}, 178 | {"version", no_argument , 0, 'v'}, 179 | {"directory", required_argument , 0, 'C'}, 180 | {"clean", no_argument , 0, 0}, 181 | {"rebuild", no_argument , 0, 0}, 182 | {"ignore-dependencies", no_argument , 0, 0}, 183 | {"make", no_argument , 0, 0}, 184 | {"vcxproj", optional_argument , 0, 0}, 185 | {"vcproj", optional_argument , 0, 0}, 186 | {"codelite", no_argument , 0, 0}, 187 | {"codeblocks", no_argument , 0, 0}, 188 | {"cmake", no_argument , 0, 0}, 189 | {"netbeans", no_argument , 0, 0}, 190 | {"jsondb", no_argument , 0, 0}, 191 | {0, 0, 0, 0} 192 | }; 193 | 194 | // find and remove all user arguments 195 | char** nargv = (char**)alloca(sizeof(char*) * argc); 196 | int nargc = 1; 197 | nargv[0] = argv[0]; 198 | for(int i = 1; i < argc; ++i) 199 | { 200 | if(strncmp(argv[i], "--", 2) != 0) 201 | nargv[nargc++] = argv[i]; 202 | else 203 | { 204 | const char* arg = argv[i] + 2; 205 | size_t arglen = strlen(arg); 206 | const char* argarg = strchr(arg, '='); 207 | if(argarg) 208 | arglen = argarg - arg; 209 | for(struct option* opt = long_options; opt->name; ++opt) 210 | if(strncmp(arg, opt->name, arglen) == 0 && strlen(opt->name) == arglen) 211 | { 212 | nargv[nargc++] = argv[i]; 213 | goto nextarg; 214 | } 215 | const char* sep = strchr(arg, '='); 216 | if(sep) 217 | { 218 | String key(arg, sep - arg); 219 | String val(sep + 1, -1); 220 | if(key == "platform") 221 | inputPlatforms.append(val); 222 | else if(key == "config") 223 | inputConfigs.append(val); 224 | else if(key == "target") 225 | inputTargets.append(val); 226 | else 227 | userArgs.append(key, val); 228 | } 229 | else 230 | userArgs.append(String(arg, -1), String()); 231 | } 232 | nextarg:; 233 | } 234 | argc = nargc; 235 | argv = nargv; 236 | 237 | // parse normal arguments 238 | while((c = getopt_long(argc, argv, "C:df:hj:v", long_options, &option_index)) != -1) 239 | switch(c) 240 | { 241 | case 0: 242 | { 243 | String opt(long_options[option_index].name, -1); 244 | if(opt == "make") 245 | generateMake = true; 246 | else if(opt == "vcxproj") 247 | { 248 | generateVcxproj = 2010; 249 | if(optarg) 250 | { 251 | if(strcmp(optarg, "2010") == 0) 252 | generateVcxproj = 2010; 253 | else if(strcmp(optarg, "2012") == 0) 254 | generateVcxproj = 2012; 255 | else if(strcmp(optarg, "2013") == 0) 256 | generateVcxproj = 2013; 257 | else if (strcmp(optarg, "2015") == 0) 258 | generateVcxproj = 2015; 259 | else if(strcmp(optarg, "2017") == 0) 260 | generateVcxproj = 2017; 261 | else if(strcmp(optarg, "2019") == 0) 262 | generateVcxproj = 2019; 263 | else // unknown version 264 | ::showHelp(argv[0]); 265 | } 266 | } 267 | else if(opt == "vcproj") 268 | { 269 | generateVcproj = 2008; 270 | if(optarg) 271 | { 272 | if(strcmp(optarg, "2008") == 0) 273 | generateVcproj = 2008; 274 | else // unknown version 275 | ::showHelp(argv[0]); 276 | } 277 | } 278 | else if(opt == "codelite") 279 | generateCodeLite = true; 280 | else if(opt == "codeblocks") 281 | generateCodeBlocks = true; 282 | else if(opt == "cmake") 283 | generateCMake = true; 284 | else if(opt == "netbeans") 285 | generateNetBeans = true; 286 | else if(opt == "jsondb") 287 | generateJsonDb = true; 288 | else if(opt == "clean") 289 | clean = true; 290 | else if(opt == "rebuild") 291 | rebuild = true; 292 | else if(opt == "ignore-dependencies") 293 | ignoreDependencies = true; 294 | } 295 | break; 296 | case 'C': 297 | inputDir = String(optarg, -1); 298 | break; 299 | case 'd': 300 | showDebug = true; 301 | break; 302 | case 'f': 303 | inputFile = String(optarg, -1); 304 | break; 305 | case 'h': 306 | showHelp = true; 307 | break; 308 | case 'j': 309 | jobs = atoi(optarg); 310 | break; 311 | case 'v': 312 | showVersion(true); 313 | break; 314 | default: 315 | ::showHelp(argv[0]); 316 | break; 317 | } 318 | while(optind < argc) 319 | { 320 | const char* arg = argv[optind++]; 321 | const char* sep = strchr(arg, '='); 322 | if(sep) 323 | { 324 | String key(arg, sep - arg); 325 | String val(sep + 1, -1); 326 | if(key == "platform") 327 | inputPlatforms.append(val); 328 | else if(key == "config") 329 | inputConfigs.append(val); 330 | else if(key == "target") 331 | inputTargets.append(val); 332 | else 333 | userArgs.append(key, val); 334 | } 335 | else 336 | { 337 | String target(arg, -1); 338 | if(target == "clean") 339 | clean = true; 340 | else if(target == "rebuild") 341 | rebuild = true; 342 | else 343 | inputTargets.append(target); 344 | } 345 | } 346 | } 347 | 348 | // change working directory? 349 | if(!inputDir.isEmpty()) 350 | { 351 | if(!Directory::change(inputDir)) 352 | { 353 | // TODO: error message 354 | return EXIT_FAILURE; 355 | } 356 | } 357 | 358 | // start the engine 359 | { 360 | Engine engine(errorHandler, argv[0]); 361 | if(!engine.load(inputFile)) 362 | { 363 | if(showHelp) 364 | showUsage(argv[0]); 365 | 366 | return EXIT_FAILURE; 367 | } 368 | 369 | // show help only? 370 | if(showHelp) 371 | { 372 | engine.enterRootKey(); 373 | if(engine.enterKey("help")) 374 | { 375 | List help; 376 | engine.getText(help); 377 | for(List::Node* node = help.getFirst(); node; node = node->getNext()) 378 | puts(node->data.getData()); 379 | return EXIT_SUCCESS; 380 | } 381 | else 382 | showUsage(argv[0]); 383 | } 384 | 385 | // generate Makefile mode? 386 | if(generateMake) 387 | { 388 | Make make(engine); 389 | if(!make.generate(userArgs)) 390 | return EXIT_FAILURE; 391 | return EXIT_SUCCESS; 392 | } 393 | 394 | // generate vcxproj mode? 395 | if(generateVcxproj) 396 | { 397 | Vcxproj vcxprog(engine, generateVcxproj); 398 | if(!vcxprog.generate(userArgs)) 399 | return EXIT_FAILURE; 400 | return EXIT_SUCCESS; 401 | } 402 | 403 | // generate vcproj mode? 404 | if(generateVcproj) 405 | { 406 | Vcproj vcproj(engine, generateVcproj); 407 | if(!vcproj.generate(userArgs)) 408 | return EXIT_FAILURE; 409 | return EXIT_SUCCESS; 410 | } 411 | 412 | // generate codelite mode? 413 | if(generateCodeLite) 414 | { 415 | CodeLite codeLite(engine); 416 | if(!codeLite.generate(userArgs)) 417 | return EXIT_FAILURE; 418 | return EXIT_SUCCESS; 419 | } 420 | 421 | // generate codeblocks mode? 422 | if(generateCodeBlocks) 423 | { 424 | CodeBlocks codeBlocks(engine); 425 | if(!codeBlocks.generate(userArgs)) 426 | return EXIT_FAILURE; 427 | return EXIT_SUCCESS; 428 | } 429 | 430 | // generate codeblocks mode? 431 | if(generateCMake) 432 | { 433 | CMake cMake(engine); 434 | if(!cMake.generate(userArgs)) 435 | return EXIT_FAILURE; 436 | return EXIT_SUCCESS; 437 | } 438 | 439 | // generate codeblocks mode? 440 | if(generateNetBeans) 441 | { 442 | NetBeans netBeans(engine); 443 | if(!netBeans.generate(userArgs)) 444 | return EXIT_FAILURE; 445 | return EXIT_SUCCESS; 446 | } 447 | 448 | // generate JSON compilation database mode? 449 | if(generateJsonDb) 450 | { 451 | JsonDb jsonDb(engine); 452 | if(!jsonDb.generate(userArgs)) 453 | return EXIT_FAILURE; 454 | return EXIT_SUCCESS; 455 | } 456 | 457 | // direct build 458 | { 459 | Mare mare(engine, inputPlatforms, inputConfigs, inputTargets, showDebug, clean, rebuild, jobs, ignoreDependencies); 460 | if(!mare.build(userArgs)) 461 | return EXIT_FAILURE; 462 | return EXIT_SUCCESS; 463 | } 464 | } 465 | } 466 | -------------------------------------------------------------------------------- /src/libmare/Parser.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "Parser.h" 6 | #include "Tools/String.h" 7 | #include "Tools/Error.h" 8 | #include "Statement.h" 9 | 10 | Parser::Parser(Engine& engine) : engine(engine), includeFile(0), readBufferPos(0), readBufferEnd(0), currentLine(1), currentChar(' ') {} 11 | 12 | Statement* Parser::parse(const String& file, Engine::ErrorHandler errorHandler, void* userData) 13 | { 14 | this->errorHandler = errorHandler; 15 | this->errorHandlerUserData = userData; 16 | this->filePath = file; 17 | 18 | try 19 | { 20 | if(!this->file.open(file)) 21 | { 22 | errorHandler(errorHandlerUserData, file, 0, Error::getString()); 23 | throw false; 24 | } 25 | includeFile = new IncludeFile(engine); 26 | includeFile->fileDir = File::getDirname(file); 27 | nextChar(); // read first character 28 | nextToken(); // read first symbol 29 | return readFile(); 30 | } 31 | catch(...) 32 | { 33 | return 0; 34 | } 35 | } 36 | 37 | void Parser::nextChar() 38 | { 39 | if(readBufferPos == readBufferEnd) 40 | { 41 | if(currentChar == '\0') 42 | return; 43 | size_t i = file.read(readBuffer, sizeof(readBuffer)); 44 | if(i == 0) 45 | { 46 | currentChar = '\0'; 47 | return; 48 | } 49 | else 50 | { 51 | readBufferPos = readBuffer; 52 | readBufferEnd = readBufferPos + i; 53 | } 54 | } 55 | currentChar = *(readBufferPos++); 56 | } 57 | 58 | void Parser::nextToken() 59 | { 60 | for(;;) 61 | { 62 | char c = currentChar; 63 | nextChar(); 64 | switch(c) 65 | { 66 | case '\0': currentToken.id = Token::eof; return; 67 | case '(': currentToken.id = Token::leftParenthesis; return; 68 | case ')': currentToken.id = Token::rightParenthesis; return; 69 | case '{': currentToken.id = Token::leftBrace; return; 70 | case '}': currentToken.id = Token::rightBrace; return; 71 | case ',': currentToken.id = Token::comma; return; 72 | case ';': currentToken.id = Token::comma; return; 73 | case '?': currentToken.id = Token::interrogation; return; 74 | case ':': currentToken.id = Token::colon; return; 75 | 76 | case '&': 77 | if(currentChar == '&') 78 | { 79 | nextChar(); 80 | currentToken.id = Token::and_; 81 | } 82 | else 83 | unexpectedChar(c); 84 | return; 85 | 86 | case '|': 87 | if(currentChar == '|') 88 | { 89 | nextChar(); 90 | currentToken.id = Token::or_; 91 | } 92 | else 93 | unexpectedChar(c); 94 | return; 95 | 96 | case '=': 97 | if(currentChar == '=') 98 | { 99 | nextChar(); 100 | currentToken.id = Token::equal; 101 | } 102 | else 103 | currentToken.id = Token::assignment; 104 | return; 105 | 106 | case '+': 107 | if(currentChar == '=') 108 | { 109 | nextChar(); 110 | currentToken.id = Token::plusAssignment; 111 | } 112 | else 113 | currentToken.id = Token::plus; 114 | return; 115 | 116 | case '-': 117 | if(currentChar == '=') 118 | { 119 | nextChar(); 120 | currentToken.id = Token::minusAssignment; 121 | } 122 | else 123 | currentToken.id = Token::minus; 124 | return; 125 | 126 | case '!': 127 | if(currentChar == '=') 128 | { 129 | nextChar(); 130 | currentToken.id = Token::notEqual; 131 | } 132 | else 133 | currentToken.id = Token::not_; 134 | return; 135 | 136 | case '>': 137 | if(currentChar == '=') 138 | { 139 | nextChar(); 140 | currentToken.id = Token::greaterEqualThan; 141 | } 142 | else 143 | currentToken.id = Token::greaterThan; 144 | return; 145 | 146 | case '<': 147 | if(currentChar == '=') 148 | { 149 | nextChar(); 150 | currentToken.id = Token::lowerEqualThan; 151 | } 152 | else 153 | currentToken.id = Token::lowerThan; 154 | return; 155 | 156 | case '/': // comment ? 157 | switch(currentChar) 158 | { 159 | case '*': // "/*" 160 | for(;;) 161 | { 162 | nextChar(); 163 | if(currentChar == '\0') 164 | goto handleNextChar; 165 | if(currentChar == '*') 166 | { 167 | nextChar(); 168 | if(currentChar == '/') 169 | { 170 | nextChar(); 171 | goto handleNextChar; 172 | } 173 | } 174 | } 175 | 176 | case '/': // "//" 177 | for(;;) 178 | { 179 | nextChar(); 180 | if(currentChar == '\0' || currentChar == '\r' || currentChar == '\n') 181 | goto handleNextChar; 182 | } 183 | 184 | default: 185 | unexpectedChar(c); 186 | goto handleNextChar; 187 | } 188 | 189 | case '\r': 190 | ++currentLine; 191 | if(currentChar == '\n') 192 | nextChar(); 193 | goto handleNextChar; 194 | case '\n': 195 | ++currentLine; 196 | goto handleNextChar; 197 | 198 | case '"': // string 199 | { 200 | currentToken.id = Token::quotedString; 201 | String& value = currentToken.value; 202 | value.clear(); 203 | while(currentChar != '"') 204 | { 205 | if(currentChar == '\\') 206 | { 207 | nextChar(); 208 | if(currentChar == '\0') 209 | goto handleNextChar; 210 | static const char backslashChars[] = "rnt\"\\"; 211 | static const char backslashTranslation[] = "\r\n\t\"\\"; 212 | const char* str = strchr(backslashChars, currentChar); 213 | if(str) 214 | value.append(backslashTranslation[str - backslashChars]); 215 | else 216 | { 217 | value.append('\\'); 218 | value.append(currentChar); 219 | } 220 | } 221 | else 222 | value.append(currentChar); 223 | nextChar(); 224 | if(currentChar == '\0') 225 | goto handleNextChar; 226 | } 227 | nextChar(); // skip closing " 228 | return; 229 | } 230 | 231 | default: // space, keyword or identifier 232 | 233 | if(isspace(*(unsigned char*)&c)) 234 | goto handleNextChar; 235 | 236 | if(isalpha(c) || c == '_') 237 | { 238 | currentToken.id = Token::string; 239 | String& value = currentToken.value; 240 | value.clear(); 241 | value.append(c); 242 | while(isalnum(currentChar) || currentChar == '_') 243 | { 244 | value.append(currentChar); 245 | nextChar(); 246 | } 247 | return; 248 | } 249 | 250 | unexpectedChar(c); 251 | goto handleNextChar; 252 | } 253 | handleNextChar: ; 254 | } 255 | } 256 | 257 | void Parser::unexpectedChar(char c) 258 | { 259 | String message; 260 | message.format(256, "unexpected character \"%c\"", c); 261 | errorHandler(errorHandlerUserData, filePath, currentLine, message); 262 | throw false; 263 | } 264 | 265 | void Parser::expectToken(Token::Id id) 266 | { 267 | if(currentToken.id != id) 268 | { 269 | String message; 270 | message.format(256, "expected \"%s\" instead of \"%s\"", currentToken.getName(id), currentToken.getName()); 271 | errorHandler(errorHandlerUserData, filePath, currentLine, message); 272 | throw false; 273 | } 274 | nextToken(); 275 | } 276 | 277 | void Parser::readString(String& string) 278 | { 279 | switch(currentToken.id) 280 | { 281 | case Token::string: 282 | case Token::quotedString: 283 | string = currentToken.value; 284 | nextToken(); 285 | return; 286 | default: 287 | { 288 | String message; 289 | message.format(256, "expected string instead of \"%s\"", currentToken.getName()); 290 | errorHandler(errorHandlerUserData, filePath, currentLine, message); 291 | throw false; 292 | } 293 | } 294 | } 295 | 296 | Statement* Parser::readFile() 297 | { 298 | BlockStatement* statement = new BlockStatement(*includeFile); 299 | while(currentToken.id != Token::eof) 300 | statement->statements.append(readStatement()); 301 | return statement; 302 | } 303 | 304 | Statement* Parser::readStatements() 305 | { 306 | if(currentToken.id == Token::leftBrace) 307 | { 308 | nextToken(); 309 | BlockStatement* blockStatement = new BlockStatement(*includeFile); 310 | while(currentToken.id != Token::rightBrace) 311 | blockStatement->statements.append(readStatement()); 312 | nextToken(); 313 | return blockStatement; 314 | } 315 | else 316 | return readStatement(); 317 | } 318 | 319 | Statement* Parser::readStatement() 320 | { 321 | Statement* statement; 322 | if(currentToken.id == Token::string && currentToken.value == "if") 323 | { 324 | nextToken(); 325 | IfStatement* ifStatement = new IfStatement(*includeFile); 326 | ifStatement->condition = readExpression(); 327 | ifStatement->thenStatements = readStatements(); 328 | if(currentToken.id == Token::string && currentToken.value == "else") 329 | { 330 | nextToken(); 331 | ifStatement->elseStatements = readStatements(); 332 | } 333 | statement = ifStatement; 334 | } 335 | else if(currentToken.id == Token::string && currentToken.value == "include") 336 | { 337 | nextToken(); 338 | 339 | String fileName; 340 | readString(fileName); 341 | if(!File::isPathAbsolute(fileName)) 342 | { 343 | String parentDir = includeFile->fileDir; 344 | if(parentDir != ".") 345 | fileName = parentDir + "/" + fileName; 346 | } 347 | 348 | Parser parser(engine); 349 | WrapperStatement* statement = new WrapperStatement(*includeFile); 350 | statement->statement = parser.parse(fileName, errorHandler, errorHandlerUserData); 351 | if(!statement->statement) 352 | throw false; 353 | return statement; 354 | } 355 | else 356 | statement = readAssignment(); 357 | while(currentToken.id == Token::comma) 358 | nextToken(); 359 | return statement; 360 | } 361 | 362 | Statement* Parser::readAssignment() 363 | { 364 | if(currentToken.id == Token::minus) 365 | { 366 | nextToken(); 367 | RemoveStatement* statement = new RemoveStatement(*includeFile); 368 | readString(statement->variable); 369 | return statement; 370 | } 371 | else 372 | { 373 | AssignStatement* statement = new AssignStatement(*includeFile); 374 | readString(statement->variable); 375 | switch(currentToken.id) 376 | { 377 | case Token::plusAssignment: 378 | case Token::minusAssignment: 379 | statement->operation = currentToken.id; 380 | // no break 381 | case Token::assignment: 382 | nextToken(); 383 | statement->value = readConcatination(); 384 | break; 385 | default: 386 | break; 387 | } 388 | return statement; 389 | } 390 | } 391 | 392 | Statement* Parser::readExpression() 393 | { 394 | Statement* statement = readOrForumla(); 395 | if(currentToken.id == Token::interrogation) 396 | { 397 | nextToken(); 398 | IfStatement* ifStatement = new IfStatement(*includeFile); 399 | ifStatement->condition = statement; 400 | ifStatement->thenStatements = readExpression(); 401 | if(currentToken.id == Token::colon) 402 | { 403 | nextToken(); 404 | ifStatement->elseStatements = readExpression(); 405 | } 406 | return ifStatement; 407 | } 408 | return statement; 409 | } 410 | 411 | Statement* Parser::readOrForumla() 412 | { 413 | Statement* statement = readAndFormula(); 414 | while(currentToken.id == Token::or_) 415 | { 416 | BinaryStatement* binaryStatement = new BinaryStatement(*includeFile); 417 | binaryStatement->operation = currentToken.id; 418 | nextToken(); 419 | binaryStatement->leftOperand = statement; 420 | binaryStatement->rightOperand = readAndFormula(); 421 | statement = binaryStatement; 422 | } 423 | return statement; 424 | } 425 | 426 | Statement* Parser::readAndFormula() 427 | { 428 | Statement* statement = readComparison(); 429 | while(currentToken.id == Token::and_) 430 | { 431 | BinaryStatement* binaryStatement = new BinaryStatement(*includeFile); 432 | binaryStatement->operation = currentToken.id; 433 | nextToken(); 434 | binaryStatement->leftOperand = statement; 435 | binaryStatement->rightOperand = readComparison(); 436 | statement = binaryStatement; 437 | } 438 | return statement; 439 | } 440 | 441 | Statement* Parser::readComparison() 442 | { 443 | Statement* statement = readRelation(); 444 | while(currentToken.id == Token::equal || currentToken.id == Token::notEqual) 445 | { 446 | BinaryStatement* binaryStatement = new BinaryStatement(*includeFile); 447 | binaryStatement->operation = currentToken.id; 448 | nextToken(); 449 | binaryStatement->leftOperand = statement; 450 | binaryStatement->rightOperand = readRelation(); 451 | statement = binaryStatement; 452 | } 453 | return statement; 454 | } 455 | 456 | Statement* Parser::readRelation() 457 | { 458 | Statement* statement = readValue(); 459 | while(currentToken.id == Token::greaterThan || currentToken.id == Token::lowerThan || 460 | currentToken.id == Token::greaterEqualThan || currentToken.id == Token::lowerEqualThan) 461 | { 462 | BinaryStatement* binaryStatement = new BinaryStatement(*includeFile); 463 | binaryStatement->operation = currentToken.id; 464 | nextToken(); 465 | binaryStatement->leftOperand = statement; 466 | binaryStatement->rightOperand = readValue(); 467 | statement = binaryStatement; 468 | } 469 | return statement; 470 | } 471 | 472 | Statement* Parser::readConcatination() 473 | { 474 | Statement* statement = readValue(); 475 | while(currentToken.id == Token::plus || currentToken.id == Token::minus) 476 | { 477 | BinaryStatement* binaryStatement = new BinaryStatement(*includeFile); 478 | binaryStatement->operation = currentToken.id; 479 | nextToken(); 480 | binaryStatement->leftOperand = statement; 481 | binaryStatement->rightOperand = readValue(); 482 | statement = binaryStatement; 483 | } 484 | return statement; 485 | } 486 | 487 | Statement* Parser::readValue() 488 | { 489 | switch(currentToken.id) 490 | { 491 | case Token::not_: 492 | { 493 | UnaryStatement* statement = new UnaryStatement(*includeFile); 494 | statement->operation = currentToken.id; 495 | nextToken(); 496 | statement->operand = readValue(); 497 | return statement; 498 | } 499 | case Token::leftParenthesis: 500 | { 501 | nextToken(); 502 | Statement* statement = readExpression(); 503 | expectToken(Token::rightParenthesis); 504 | return statement; 505 | } 506 | case Token::leftBrace: 507 | { 508 | nextToken(); 509 | BlockStatement* statements = new BlockStatement(*includeFile); 510 | while(currentToken.id != Token::rightBrace) 511 | { 512 | if(currentToken.id == Token::eof) 513 | expectToken(Token::rightBrace); 514 | statements->statements.append(readStatement()); 515 | } 516 | nextToken(); 517 | return statements; 518 | } 519 | case Token::string: 520 | if(currentToken.value == "true") 521 | { 522 | nextToken(); 523 | StringStatement* statement = new StringStatement(*includeFile); 524 | statement->value = "true"; 525 | return statement; 526 | } 527 | else if(currentToken.value == "false") 528 | { 529 | nextToken(); 530 | return new StringStatement(*includeFile); 531 | } 532 | else 533 | { 534 | ReferenceStatement* statement = new ReferenceStatement(*includeFile); 535 | readString(statement->variable); 536 | return statement; 537 | } 538 | default: // quotedString 539 | { 540 | StringStatement* statement = new StringStatement(*includeFile); 541 | readString(statement->value); 542 | return statement; 543 | } 544 | } 545 | } 546 | 547 | -------------------------------------------------------------------------------- /src/mare/NetBeans.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | //#include 5 | 6 | #include "Engine.h" 7 | 8 | #include "Tools/Assert.h" 9 | #include "Tools/Error.h" 10 | //#include "Tools/Word.h" 11 | #include "Tools/Directory.h" 12 | 13 | #include "NetBeans.h" 14 | 15 | bool NetBeans::generate(const Map& userArgs) 16 | { 17 | engine.addDefaultKey("tool", "NetBeans"); 18 | engine.addDefaultKey("NetBeans", "NetBeans"); 19 | #if defined(_WIN32) || defined(__CYGWIN__) 20 | String platform("Win32"); 21 | #elif defined(__linux) 22 | String platform("Linux"); 23 | #elif defined(__APPLE__) && defined(__MACH__) 24 | String platform("MacOSX"); 25 | #else 26 | String platform("unknown"); 27 | // add your os :) 28 | // http://predef.sourceforge.net/preos.html 29 | #endif 30 | engine.addDefaultKey("host", platform); // the platform on which the compiler is run 31 | engine.addDefaultKey("platforms", platform); // the target platform of the compiler 32 | engine.addDefaultKey("configurations", "Debug Release"); 33 | engine.addDefaultKey("targets"); // an empty target list exists per default 34 | engine.addDefaultKey("buildDir", "$(configuration)"); 35 | engine.addDefaultKey("outputDir", "$(buildDir)"); 36 | 37 | { 38 | Map cApplication; 39 | cApplication.append("command", "__Application"); 40 | cApplication.append("output", "$(outputDir)/$(target)$(if $(Win32),.exe)"); 41 | engine.addDefaultKey("cppApplication", cApplication); 42 | engine.addDefaultKey("cApplication", cApplication); 43 | } 44 | { 45 | Map cDynamicLibrary; 46 | cDynamicLibrary.append("command", "__DynamicLibrary"); 47 | cDynamicLibrary.append("output", "$(outputDir)/$(if $(Win32),,lib)$(patsubst lib%,%,$(target))$(if $(Win32),.dll,.so)"); 48 | engine.addDefaultKey("cppDynamicLibrary", cDynamicLibrary); 49 | engine.addDefaultKey("cDynamicLibrary", cDynamicLibrary); 50 | } 51 | { 52 | Map cStaticLibrary; 53 | cStaticLibrary.append("command", "__StaticLibrary"); 54 | cStaticLibrary.append("output", "$(outputDir)/$(if $(Win32),,lib)$(patsubst lib%,%,$(target))$(if $(Win32),.lib,.a)"); 55 | engine.addDefaultKey("cppStaticLibrary", cStaticLibrary); 56 | engine.addDefaultKey("cStaticLibrary", cStaticLibrary); 57 | } 58 | 59 | // add user arguments 60 | for(const Map::Node* i = userArgs.getFirst(); i; i = i->getNext()) 61 | engine.addCommandLineKey(i->key, i->data); 62 | 63 | // step #1: read project data from marefile 64 | if(!readFile()) 65 | return false; 66 | 67 | // step #2: ... 68 | if(!processData()) 69 | return false; 70 | 71 | // step #3: generate project files 72 | for(Map::Node* i = projects.getFirst(); i; i = i->getNext()) 73 | if(!generateProject(i->data)) 74 | return false; 75 | 76 | return true; 77 | } 78 | 79 | bool NetBeans::readFile() 80 | { 81 | // get some global keys 82 | engine.enterRootKey(); 83 | List allPlatforms, allConfigurations, allTargets; 84 | engine.getKeys("platforms", allPlatforms); 85 | engine.getKeys("configurations", allConfigurations); 86 | engine.getKeys("targets", allTargets); 87 | engine.leaveKey(); 88 | 89 | // do something for each target in each configuration 90 | for(const List::Node* i = allPlatforms.getFirst(); i; i = 0) // just use the first platform 91 | { 92 | const String& platform = i->data; 93 | for(const List::Node* i = allConfigurations.getFirst(); i; i = i->getNext()) 94 | { 95 | const String& configName = i->data; 96 | configs.append(configName); 97 | 98 | for(const List::Node* i = allTargets.getFirst(); i; i = i->getNext()) 99 | { 100 | engine.enterUnnamedKey(); 101 | engine.addDefaultKey("platform", platform); 102 | engine.addDefaultKey(platform, platform); 103 | engine.addDefaultKey("configuration", configName); 104 | engine.addDefaultKey(configName, configName); 105 | engine.addDefaultKey("target", i->data); 106 | //engine.addDefaultKey(i->data, i->data); 107 | engine.enterRootKey(); 108 | VERIFY(engine.enterKey("targets")); 109 | if(!engine.enterKey(i->data)) 110 | { 111 | engine.error(String().format(256, "cannot find target \"%s\"", i->data.getData())); 112 | return false; 113 | } 114 | engine.addDefaultKey("mareDir", engine.getMareDir()); 115 | 116 | Map::Node* node = projects.find(i->data); 117 | Project& project = node ? node->data : projects.append(i->data, Project(i->data)); 118 | 119 | Project::Config& projectConfig = project.configs.append(configName, Project::Config(configName)); 120 | 121 | engine.getText("buildCommand", projectConfig.buildCommand, false); 122 | engine.getText("reBuildCommand", projectConfig.reBuildCommand, false); 123 | engine.getText("cleanCommand", projectConfig.cleanCommand, false); 124 | projectConfig.buildDir = engine.getFirstKey("buildDir", true); 125 | projectConfig.firstOutput = engine.getFirstKey("output", false); 126 | engine.getKeys("defines", projectConfig.defines, true); 127 | engine.getKeys("includePaths", projectConfig.includePaths, true); 128 | //engine.getKeys("libPaths", projectConfig.libPaths, true); 129 | //engine.getKeys("libs", projectConfig.libs, true); 130 | 131 | List dependencies; 132 | engine.getKeys("dependencies", dependencies, false); 133 | for(const List::Node* i = dependencies.getFirst(); i; i = i->getNext()) 134 | if(!project.dependencies.find(i->data)) 135 | project.dependencies.append(i->data); 136 | 137 | List root; 138 | engine.getKeys("root", root, true); 139 | for(const List::Node* i = root.getFirst(); i; i = i->getNext()) 140 | if(!project.roots.find(i->data)) 141 | project.roots.append(i->data); 142 | 143 | if(engine.enterKey("files")) 144 | { 145 | List files; 146 | engine.getKeys(files); 147 | for(const List::Node* i = files.getFirst(); i; i = i->getNext()) 148 | { 149 | Map::Node* node = project.files.find(i->data); 150 | Project::File& file = node ? node->data : project.files.append(i->data, Project::File(i->data)); 151 | engine.enterUnnamedKey(); 152 | engine.addDefaultKey("file", i->data); 153 | VERIFY(engine.enterKey(i->data)); 154 | file.folder = engine.getFirstKey("folder", false); 155 | engine.leaveKey(); // VERIFY(engine.enterKey(i->data)); 156 | engine.leaveKey(); 157 | } 158 | 159 | engine.leaveKey(); 160 | } 161 | 162 | engine.leaveKey(); 163 | engine.leaveKey(); 164 | engine.leaveKey(); 165 | engine.leaveKey(); 166 | } 167 | } 168 | } 169 | 170 | return true; 171 | } 172 | 173 | bool NetBeans::processData() 174 | { 175 | // remove projects not creating any output files 176 | for(Map::Node* i = projects.getFirst(), * nexti; i; i = nexti) 177 | { 178 | nexti = i->getNext(); 179 | if(!i->data.files.isEmpty()) 180 | continue; 181 | for(const Map::Node* j = i->data.configs.getFirst(); j; j = j->getNext()) 182 | if(!j->data.firstOutput.isEmpty()) 183 | goto next; 184 | projects.remove(i); 185 | next:; 186 | } 187 | 188 | // 189 | if(projects.isEmpty()) 190 | { 191 | engine.error("cannot find any targets"); 192 | return false; 193 | } 194 | 195 | return true; 196 | } 197 | 198 | bool NetBeans::generateProject(Project& project) 199 | { 200 | Directory::create(project.name); 201 | Directory::create(project.name + "/nbproject"); 202 | 203 | fileOpen(project.name + "/nbproject/project.xml"); 204 | fileWrite("\n"); 205 | fileWrite("\n"); 206 | fileWrite(" org.netbeans.modules.cnd.makeproject\n"); 207 | fileWrite(" \n"); 208 | fileWrite(" \n"); 209 | fileWrite(String(" ") + xmlEscape(project.name) + "\n"); 210 | fileWrite(" c\n"); 211 | fileWrite(" cc,cpp,mm\n"); 212 | fileWrite(" h\n"); 213 | //fileWrite(" UTF-8\n"); 214 | fileWrite(" ISO-8859-1\n"); 215 | 216 | fileWrite(" \n"); 217 | for(const Map::Node* i = project.dependencies.getFirst(); i; i = i->getNext()) 218 | fileWrite(String(" ../") + xmlEscape(i->key) + "\n"); 219 | fileWrite(" \n"); 220 | 221 | fileWrite(" \n"); 222 | for(const Map::Node* i = project.roots.getFirst(); i; i = i->getNext()) 223 | fileWrite(String(" ../") + xmlEscape(i->key) + "\n"); 224 | fileWrite(" \n"); 225 | fileWrite(" \n"); 226 | for(const Map::Node* i = project.configs.getFirst(); i; i = i->getNext()) 227 | { 228 | fileWrite(" \n"); 229 | fileWrite(String(" ") + xmlEscape(i->key) + "\n"); 230 | fileWrite(" 0\n"); 231 | fileWrite(" \n"); 232 | } 233 | fileWrite(" \n"); 234 | fileWrite(" \n"); 235 | fileWrite(" \n"); 236 | fileWrite("\n"); 237 | fileClose(); 238 | 239 | fileOpen(project.name + "/nbproject/configurations.xml"); 240 | fileWrite("\n"); 241 | //fileWrite("\n"); 242 | fileWrite("\n"); 243 | fileWrite(" \n"); 244 | for(const Map::Node* i = project.roots.getFirst(); i; i = i->getNext()) 245 | { 246 | fileWrite(String(" key)) + "\" root=\"../" + xmlEscape(i->key) + "\">\n"); 247 | //fileWrite(" \n"); 248 | //fileWrite(" \n"); 249 | //fileWrite(" MacStyle.h\n"); 250 | //fileWrite(" MacStyle.mm\n"); 251 | //fileWrite(" Main.cpp\n"); 252 | //fileWrite(" MainWindow.cpp\n"); 253 | //fileWrite(" MainWindow.h\n"); 254 | //fileWrite(" RegisteredDockWidget.cpp\n"); 255 | //fileWrite(" RegisteredDockWidget.h\n"); 256 | //fileWrite(" SceneGraphDockWidget.cpp\n"); 257 | //fileWrite(" SceneGraphDockWidget.h\n"); 258 | //fileWrite(" SimRobot.h\n"); 259 | //fileWrite(" StatusBar.cpp\n"); 260 | //fileWrite(" StatusBar.h\n"); 261 | //fileWrite(" qtdotnetstyle.cpp\n"); 262 | //fileWrite(" qtdotnetstyle.h\n"); 263 | //fileWrite(" resource.h\n"); 264 | fileWrite(" \n"); 265 | } 266 | fileWrite(" \n"); 267 | fileWrite(" ^(nbproject)$\n"); 268 | fileWrite(" \n"); 269 | for(const Map::Node* i = project.roots.getFirst(); i; i = i->getNext()) 270 | fileWrite(String(" ../") + xmlEscape(i->key) + "\n");; 271 | fileWrite(" \n"); 272 | fileWrite(" Makefile\n"); 273 | fileWrite(" \n"); 274 | for(const Map::Node* i = project.configs.getFirst(); i; i = i->getNext()) 275 | { 276 | const Project::Config& projectConfig = i->data; 277 | fileWrite(String(" \n"); 278 | fileWrite(" \n"); 279 | fileWrite(" LOCAL_SOURCES\n"); 280 | fileWrite(" default\n"); 281 | fileWrite(" \n"); 282 | fileWrite(" \n"); 283 | fileWrite(" \n"); 284 | fileWrite(" ..\n"); 285 | fileWrite(String(" ") + joinCommands(projectConfig.buildCommand) + "\n"); 286 | fileWrite(String(" ") + joinCommands(projectConfig.cleanCommand) + "\n"); 287 | fileWrite(String(" ../") + xmlEscape(projectConfig.firstOutput) + "\n"); 288 | fileWrite(" \n"); 289 | fileWrite(" \n"); 290 | for(const List::Node* i = projectConfig.includePaths.getFirst(); i; i = i->getNext()) 291 | fileWrite(String(" ") + xmlEscape(i->data) + "\n"); 292 | fileWrite(" \n"); 293 | fileWrite(" \n"); 294 | for(const List::Node* i = projectConfig.defines.getFirst(); i; i = i->getNext()) 295 | fileWrite(String(" ") + xmlEscape(i->data) + "\n"); 296 | fileWrite(" \n"); 297 | fileWrite(" \n"); 298 | fileWrite(" \n"); 299 | fileWrite(" \n"); 300 | for(const List::Node* i = projectConfig.includePaths.getFirst(); i; i = i->getNext()) 301 | fileWrite(String(" ") + xmlEscape(i->data) + "\n"); 302 | fileWrite(" \n"); 303 | fileWrite(" \n"); 304 | for(const List::Node* i = projectConfig.defines.getFirst(); i; i = i->getNext()) 305 | fileWrite(String(" ") + xmlEscape(i->data) + "\n"); 306 | fileWrite(" \n"); 307 | fileWrite(" \n"); 308 | fileWrite(" \n"); 309 | 310 | fileWrite(" \n"); 311 | for(const Map::Node* i = project.dependencies.getFirst(); i; i = i->getNext()) 312 | { 313 | const Map::Node* otherProjectNode = projects.find(i->key); 314 | const Map::Node* otherProjectConfigNode = otherProjectNode ? otherProjectNode->data.configs.find(projectConfig.name) : 0; 315 | const Project::Config* otherProjectConfig = otherProjectConfigNode ? &otherProjectConfigNode->data : 0; 316 | if(!otherProjectConfig) 317 | continue; 318 | fileWrite(String(" key) + "\"\n"); 319 | fileWrite(" CT=\"0\"\n"); 320 | fileWrite(String(" CN=\"") + xmlEscape(projectConfig.name) + "\"\n"); 321 | fileWrite(" AC=\"true\"\n"); 322 | fileWrite(" BL=\"false\"\n"); 323 | fileWrite(" WD=\"..\"\n"); 324 | fileWrite(String(" BC=\"") + joinCommands(otherProjectConfig->buildCommand) + "\"\n"); 325 | fileWrite(String(" CC=\"") + joinCommands(otherProjectConfig->cleanCommand) + "\"\n"); 326 | fileWrite(String(" OP=\"") + xmlEscape(otherProjectConfig->firstOutput) + "\">\n"); 327 | fileWrite(" \n"); 328 | } 329 | fileWrite(" \n"); 330 | 331 | fileWrite(" \n"); 332 | fileWrite(" \n"); 333 | } 334 | fileWrite(" \n"); 335 | fileWrite("\n"); 336 | fileClose(); 337 | return true; 338 | } 339 | 340 | void NetBeans::fileOpen(const String& name) 341 | { 342 | if(!file.open(name, File::writeFlag)) 343 | { 344 | engine.error(Error::getString()); 345 | exit(EXIT_FAILURE); 346 | } 347 | openedFile = name; 348 | } 349 | 350 | void NetBeans::fileWrite(const String& data) 351 | { 352 | if(!file.write(data)) 353 | { 354 | engine.error(Error::getString()); 355 | exit(EXIT_FAILURE); 356 | } 357 | } 358 | 359 | void NetBeans::fileClose() 360 | { 361 | file.close(); 362 | if(!openedFile.isEmpty()) 363 | { 364 | puts(openedFile.getData()); 365 | fflush(stdout); 366 | } 367 | openedFile.clear(); 368 | } 369 | /* 370 | String NetBeans::join(const List& items, char sep, const String& suffix) 371 | { 372 | String result; 373 | const List::Node* i = items.getFirst(); 374 | if(i) 375 | { 376 | result = xmlEscape(i->data); 377 | result.append(suffix); 378 | for(i = i->getNext(); i; i = i->getNext()) 379 | { 380 | result.append(sep); 381 | result.append(xmlEscape(i->data)); 382 | result.append(suffix); 383 | } 384 | } 385 | return result; 386 | } 387 | */ 388 | String NetBeans::joinCommands(const List& commands) 389 | { 390 | String result; 391 | for(const List::Node* i = commands.getFirst(); i; i = i->getNext()) 392 | { 393 | if(i->data.isEmpty()) 394 | continue; 395 | if(!result.isEmpty()) 396 | result.append(" && "); 397 | result.append(xmlEscape(i->data)); 398 | } 399 | return result; 400 | } 401 | 402 | String NetBeans::xmlEscape(const String& text) 403 | { 404 | const char* str = text.getData(); 405 | for(; *str; ++str) 406 | if(*str == '<' || *str == '>' || *str == '&') 407 | goto escape; 408 | return text; 409 | escape: 410 | String result(text); 411 | result.setLength(str - text.getData()); 412 | for(; *str; ++str) 413 | switch(*str) 414 | { 415 | case '<': result.append("<"); break; 416 | case '>': result.append(">"); break; 417 | case '&': result.append("&"); break; 418 | default: result.append(*str); break; 419 | } 420 | return result; 421 | } 422 | -------------------------------------------------------------------------------- /src/mare/CodeBlocks.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Engine.h" 8 | 9 | #include "Tools/Assert.h" 10 | #include "Tools/Error.h" 11 | #include "Tools/Word.h" 12 | 13 | #include "CodeBlocks.h" 14 | 15 | bool CodeBlocks::generate(const Map& userArgs) 16 | { 17 | engine.addDefaultKey("tool", "CodeBlocks"); 18 | engine.addDefaultKey("CodeBlocks", "CodeBlocks"); 19 | #if defined(_WIN32) || defined(__CYGWIN__) 20 | String platform("Win32"); 21 | #elif defined(__linux) 22 | String platform("Linux"); 23 | #elif defined(__APPLE__) && defined(__MACH__) 24 | String platform("MacOSX"); 25 | #else 26 | String platform("unknown"); 27 | // add your os :) 28 | // http://predef.sourceforge.net/preos.html 29 | #endif 30 | engine.addDefaultKey("host", platform); // the platform on which the compiler is run 31 | engine.addDefaultKey("platforms", platform); // the target platform of the compiler 32 | engine.addDefaultKey("configurations", "Debug Release"); 33 | engine.addDefaultKey("targets"); // an empty target list exists per default 34 | engine.addDefaultKey("buildDir", "$(configuration)"); 35 | engine.addDefaultKey("outputDir", "$(buildDir)"); 36 | 37 | { 38 | Map cApplication; 39 | cApplication.append("command", "__Application"); 40 | cApplication.append("output", "$(outputDir)/$(target)$(if $(Win32),.exe)"); 41 | engine.addDefaultKey("cppApplication", cApplication); 42 | engine.addDefaultKey("cApplication", cApplication); 43 | } 44 | { 45 | Map cDynamicLibrary; 46 | cDynamicLibrary.append("command", "__DynamicLibrary"); 47 | cDynamicLibrary.append("output", "$(outputDir)/$(if $(Win32),,lib)$(patsubst lib%,%,$(target))$(if $(Win32),.dll,.so)"); 48 | engine.addDefaultKey("cppDynamicLibrary", cDynamicLibrary); 49 | engine.addDefaultKey("cDynamicLibrary", cDynamicLibrary); 50 | } 51 | { 52 | Map cStaticLibrary; 53 | cStaticLibrary.append("command", "__StaticLibrary"); 54 | cStaticLibrary.append("output", "$(outputDir)/$(if $(Win32),,lib)$(patsubst lib%,%,$(target))$(if $(Win32),.lib,.a)"); 55 | engine.addDefaultKey("cppStaticLibrary", cStaticLibrary); 56 | engine.addDefaultKey("cStaticLibrary", cStaticLibrary); 57 | } 58 | 59 | // add user arguments 60 | for(const Map::Node* i = userArgs.getFirst(); i; i = i->getNext()) 61 | engine.addCommandLineKey(i->key, i->data); 62 | 63 | // step #1: read input file 64 | if(!readFile()) 65 | return false; 66 | 67 | // step #2: ... 68 | if(!processData()) 69 | return false; 70 | 71 | // step #3: generate workspace and project file 72 | if(!generateWorkspace()) 73 | return false; 74 | if(!generateProjects()) 75 | return false; 76 | 77 | return true; 78 | } 79 | 80 | bool CodeBlocks::readFile() 81 | { 82 | // get some global keys 83 | engine.enterRootKey(); 84 | workspaceName = engine.getFirstKey("name"); 85 | List allPlatforms, allConfigurations, allTargets; 86 | engine.getKeys("platforms", allPlatforms); 87 | engine.getKeys("configurations", allConfigurations); 88 | engine.getKeys("targets", allTargets); 89 | engine.leaveKey(); 90 | 91 | // do something for each target in each configuration 92 | for(const List::Node* i = allPlatforms.getFirst(); i; i = 0) // just use the first platform since CodeBlocks does not really support multiple target platforms 93 | { 94 | const String& platform = i->data; 95 | for(const List::Node* i = allConfigurations.getFirst(); i; i = i->getNext()) 96 | { 97 | const String& configName = i->data; 98 | configs.append(configName); 99 | 100 | for(const List::Node* i = allTargets.getFirst(); i; i = i->getNext()) 101 | { 102 | engine.enterUnnamedKey(); 103 | engine.addDefaultKey("platform", platform); 104 | engine.addDefaultKey(platform, platform); 105 | engine.addDefaultKey("configuration", configName); 106 | engine.addDefaultKey(configName, configName); 107 | engine.addDefaultKey("target", i->data); 108 | //engine.addDefaultKey(i->data, i->data); 109 | engine.enterRootKey(); 110 | VERIFY(engine.enterKey("targets")); 111 | if(!engine.enterKey(i->data)) 112 | { 113 | engine.error(String().format(256, "cannot find target \"%s\"", i->data.getData())); 114 | return false; 115 | } 116 | engine.addDefaultKey("mareDir", engine.getMareDir()); 117 | 118 | Map::Node* node = projects.find(i->data); 119 | Project& project = node ? node->data : projects.append(i->data, Project(i->data)); 120 | //bool isNewProject = node && true; 121 | 122 | Project::Config& projectConfig = project.configs.append(configName, Project::Config(configName)); 123 | 124 | /* 125 | if(isNewProject) 126 | { 127 | String filterName = engine.getFirstKey("folder", false); 128 | if(!filterName.isEmpty()) 129 | { 130 | Map::Node* node = projectFilters.find(filterName); 131 | ProjectFilter& filter = node ? node->data : projectFilters.append(filterName, ProjectFilter(createSomethingLikeGUID(filterName))); 132 | filter.projects.append(project); 133 | } 134 | } 135 | */ 136 | 137 | engine.getText("buildCommand", projectConfig.buildCommand, false); 138 | engine.getText("reBuildCommand", projectConfig.reBuildCommand, false); 139 | engine.getText("cleanCommand", projectConfig.cleanCommand, false); 140 | projectConfig.buildDir = engine.getFirstKey("buildDir", true); 141 | 142 | engine.getText("command", projectConfig.command, false); 143 | projectConfig.firstOutput = engine.getFirstKey("output", false); 144 | 145 | if(!projectConfig.command.isEmpty()) 146 | { 147 | String firstCommand = projectConfig.command.getFirst()->data; 148 | if(firstCommand == "__Custom" || firstCommand == "__Application" || firstCommand == "__StaticLibrary" || firstCommand == "__DynamicLibrary") 149 | { 150 | if(firstCommand == "__Custom") 151 | { 152 | projectConfig.customBuild = true; 153 | firstCommand = (projectConfig.command.getSize() > 1) ? projectConfig.command.getFirst()->getNext()->data : String(); 154 | } 155 | 156 | if(firstCommand == "__Application") 157 | projectConfig.type = "Executable"; 158 | else if(firstCommand == "__StaticLibrary") 159 | projectConfig.type = "Static Library"; 160 | else if(firstCommand == "__DynamicLibrary") 161 | projectConfig.type = "Dynamic Library"; 162 | projectConfig.command.clear(); 163 | } 164 | } 165 | if(!projectConfig.buildCommand.isEmpty()) 166 | projectConfig.customBuild = true; 167 | 168 | /* 169 | engine.getKeys("cppFlags", projectConfig.cppFlags, true); 170 | List linkFlags; 171 | engine.getKeys("linkFlags", linkFlags, true); 172 | for(const List::Node* i = linkFlags.getFirst(); i; i = i->getNext()) 173 | projectConfig.linkFlags.append(i->data, 0); 174 | projectConfig.firstOutput = engine.getFirstKey("output", false); 175 | engine.getKeys("defines", projectConfig.defines, true); 176 | engine.getKeys("includePaths", projectConfig.includePaths, true); 177 | engine.getKeys("libPaths", projectConfig.libPaths, true); 178 | engine.getKeys("libs", projectConfig.libs, true); 179 | */ 180 | List dependencies; 181 | engine.getKeys("dependencies", dependencies, false); 182 | for(const List::Node* i = dependencies.getFirst(); i; i = i->getNext()) 183 | if(!project.dependencies.find(i->data)) 184 | project.dependencies.append(i->data); 185 | List root; 186 | engine.getKeys("root", root, true); 187 | for(const List::Node* i = root.getFirst(); i; i = i->getNext()) 188 | project.roots.append(i->data); 189 | if(engine.enterKey("files")) 190 | { 191 | List files; 192 | engine.getKeys(files); 193 | for(const List::Node* i = files.getFirst(); i; i = i->getNext()) 194 | { 195 | Map::Node* node = project.files.find(i->data); 196 | Project::File& file = node ? node->data : project.files.append(i->data, Project::File(i->data)); 197 | engine.enterUnnamedKey(); 198 | engine.addDefaultKey("file", i->data); 199 | VERIFY(engine.enterKey(i->data)); 200 | file.folder = engine.getFirstKey("folder", false); 201 | engine.leaveKey(); // VERIFY(engine.enterKey(i->data)); 202 | engine.leaveKey(); 203 | } 204 | 205 | engine.leaveKey(); 206 | } 207 | 208 | engine.leaveKey(); 209 | engine.leaveKey(); 210 | engine.leaveKey(); 211 | engine.leaveKey(); 212 | } 213 | } 214 | } 215 | 216 | return true; 217 | } 218 | 219 | bool CodeBlocks::processData() 220 | { 221 | return true; 222 | } 223 | 224 | bool CodeBlocks::generateWorkspace() 225 | { 226 | // remove projects not creating any output files 227 | for(Map::Node* i = projects.getFirst(), * nexti; i; i = nexti) 228 | { 229 | nexti = i->getNext(); 230 | if(!i->data.files.isEmpty()) 231 | continue; 232 | for(const Map::Node* j = i->data.configs.getFirst(); j; j = j->getNext()) 233 | if(!j->data.command.isEmpty() || !j->data.firstOutput.isEmpty() || !j->data.type.isEmpty()) 234 | goto next; 235 | projects.remove(i); 236 | next:; 237 | } 238 | 239 | // avoid creating an empty and possibly nameless solution file 240 | if(projects.isEmpty()) 241 | { 242 | engine.error("cannot find any targets"); 243 | return false; 244 | } 245 | 246 | // create solution file name 247 | if(workspaceName.isEmpty() && !projects.isEmpty()) 248 | workspaceName = projects.getFirst()->data.name; 249 | 250 | // open output file 251 | fileOpen(workspaceName + ".workspace"); 252 | 253 | // write 254 | fileWrite("\n"); 255 | fileWrite("\n"); 256 | fileWrite(String("\t\n"); 257 | for(const Map::Node* i = projects.getFirst(); i; i = i->getNext()) 258 | { 259 | const Project& project = i->data; 260 | fileWrite(String("\t\t\n"); 261 | for(const Map::Node* i = project.dependencies.getFirst(); i; i = i->getNext()) 262 | fileWrite(String("\t\t\tkey + ".cbp\" />\n"); 263 | fileWrite("\t\t\n"); 264 | } 265 | fileWrite("\t\n"); 266 | fileWrite("\n"); 267 | 268 | // 269 | fileClose(); 270 | return true; 271 | } 272 | 273 | bool CodeBlocks::generateProjects() 274 | { 275 | for(Map::Node* i = projects.getFirst(); i; i = i->getNext()) 276 | if(!generateProject(i->data)) 277 | return false; 278 | return true; 279 | } 280 | 281 | bool CodeBlocks::generateProject(Project& project) 282 | { 283 | fileOpen(project.name + ".cbp"); 284 | 285 | fileWrite("\n"); 286 | fileWrite("\n"); 287 | fileWrite("\t\n"); 288 | 289 | fileWrite("\t\n"); 290 | fileWrite(String("\t\t\n"); 403 | 404 | fileWrite("\n"); 405 | 406 | fileClose(); 407 | return true; 408 | } 409 | 410 | void CodeBlocks::fileOpen(const String& name) 411 | { 412 | if(!file.open(name, File::writeFlag)) 413 | { 414 | engine.error(Error::getString()); 415 | exit(EXIT_FAILURE); 416 | } 417 | openedFile = name; 418 | } 419 | 420 | void CodeBlocks::fileWrite(const String& data) 421 | { 422 | if(!file.write(data)) 423 | { 424 | engine.error(Error::getString()); 425 | exit(EXIT_FAILURE); 426 | } 427 | } 428 | 429 | void CodeBlocks::fileClose() 430 | { 431 | file.close(); 432 | if(!openedFile.isEmpty()) 433 | { 434 | puts(openedFile.getData()); 435 | fflush(stdout); 436 | } 437 | openedFile.clear(); 438 | } 439 | 440 | String CodeBlocks::join(const List& items, char sep, const String& suffix) 441 | { 442 | String result; 443 | const List::Node* i = items.getFirst(); 444 | if(i) 445 | { 446 | result = xmlEscape(i->data); 447 | result.append(suffix); 448 | for(i = i->getNext(); i; i = i->getNext()) 449 | { 450 | result.append(sep); 451 | result.append(xmlEscape(i->data)); 452 | result.append(suffix); 453 | } 454 | } 455 | return result; 456 | } 457 | 458 | String CodeBlocks::joinCommands(const List& commands) 459 | { 460 | String result; 461 | for(const List::Node* i = commands.getFirst(); i; i = i->getNext()) 462 | { 463 | if(i->data.isEmpty()) 464 | continue; 465 | if(!result.isEmpty()) 466 | result.append(" && "); 467 | result.append(xmlEscape(i->data)); 468 | } 469 | return result; 470 | } 471 | 472 | String CodeBlocks::xmlEscape(const String& text) 473 | { 474 | const char* str = text.getData(); 475 | for(; *str; ++str) 476 | if(*str == '<' || *str == '>' || *str == '&') 477 | goto escape; 478 | return text; 479 | escape: 480 | String result(text); 481 | result.setLength(str - text.getData()); 482 | for(; *str; ++str) 483 | switch(*str) 484 | { 485 | case '<': result.append("<"); break; 486 | case '>': result.append(">"); break; 487 | case '&': result.append("&"); break; 488 | default: result.append(*str); break; 489 | } 490 | return result; 491 | } 492 | --------------------------------------------------------------------------------