aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/ninja/Ninja.cpp14
-rw-r--r--include/Package.hpp12
-rw-r--r--src/Conf.cpp8
-rw-r--r--src/FileUtil.cpp1
-rw-r--r--src/GlobalLib.cpp17
-rw-r--r--src/main.cpp268
6 files changed, 228 insertions, 92 deletions
diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp
index 7fb07bf..87dd873 100644
--- a/backend/ninja/Ninja.cpp
+++ b/backend/ninja/Ninja.cpp
@@ -129,9 +129,9 @@ namespace backend
}
}
- globalLibLinkerFlags += " ";
+ globalLibLinkerFlags += " '";
globalLibLinkerFlags += globalLibLinkerFlagsResult.unwrap();
- // TODO: If package doesn't exist, download it from github/server
+ globalLibLinkerFlags += "'";
}
}
@@ -139,7 +139,8 @@ namespace backend
if(pkgConfigLinkerFlagsResult.isErr())
return pkgConfigLinkerFlagsResult;
- linkerFlagCallbackFunc(pkgConfigLinkerFlagsResult.unwrap());
+ if(!pkgConfigLinkerFlagsResult.unwrap().empty())
+ linkerFlagCallbackFunc(pkgConfigLinkerFlagsResult.unwrap());
string allLinkerFlags = pkgConfigLinkerFlagsResult.unwrap();
allLinkerFlags += globalLibLinkerFlags;
@@ -193,7 +194,7 @@ namespace backend
}
default:
assert(false);
- return Result<bool>::Err("NOT IMPLEMENTED YET!");
+ return Result<bool>::Err("Building a dynamic library is not supported yet");
}
vector<string> objectNames;
@@ -224,8 +225,9 @@ namespace backend
string allLinkerFlags;
Result<string> linkerFlags = getLinkerFlags(dependencies, [&allLinkerFlags](const string &linkerFlag)
{
- allLinkerFlags += " ";
+ allLinkerFlags += " '";
allLinkerFlags += linkerFlag;
+ allLinkerFlags += "'";
});
if(linkerFlags.isErr())
return Result<bool>::Err(linkerFlags.getErrMsg());
@@ -256,7 +258,7 @@ namespace backend
}
default:
assert(false);
- return Result<bool>::Err("NOT IMPLEMENTED YET!");
+ return Result<bool>::Err("Building a dynamic library is not supported yet");
}
Result<bool> fileOverwriteResult = sibs::fileOverwrite(ninjaBuildFilePath.c_str(), sibs::StringView(result.data(), result.size()));
diff --git a/include/Package.hpp b/include/Package.hpp
index 130e987..106a48d 100644
--- a/include/Package.hpp
+++ b/include/Package.hpp
@@ -5,7 +5,19 @@ namespace sibs
{
enum class PackageType : int
{
+ // Compile as executable when compiling project with this directly.
+ // If used in dependency, then fail because you can't (currently) have dependency to executable.
EXECUTABLE,
+
+ // Compile as static library when compiling project with this directly.
+ // If used in dependency, then this is the preferred library type, but the dependant project can override this.
+ STATIC,
+
+ // Compile as dynamic library when compiling project with this directly.
+ // If used in dependency, then this is the preferred library type, but the dependant project can override this.
+ DYNAMIC,
+
+ // Identical to DYNAMIC
LIBRARY
};
}
diff --git a/src/Conf.cpp b/src/Conf.cpp
index 8dd7608..1f439cd 100644
--- a/src/Conf.cpp
+++ b/src/Conf.cpp
@@ -378,11 +378,15 @@ namespace sibs
const StringView &packageTypeStr = value.asSingle();
if(packageTypeStr.equals("executable"))
packageType = PackageType::EXECUTABLE;
+ else if(packageTypeStr.equals("static"))
+ packageType = PackageType::STATIC;
+ else if(packageTypeStr.equals("dynamic"))
+ packageType = PackageType::DYNAMIC;
else if(packageTypeStr.equals("library"))
packageType = PackageType::LIBRARY;
else
{
- string errMsg = "Expected package.type to be either 'executable' or 'library', was: ";
+ string errMsg = "Expected package.type to be either 'executable', 'static', 'dynamic' or 'library', was: ";
errMsg += string(packageTypeStr.data, packageTypeStr.size);
throw ParserException(errMsg);
}
@@ -409,7 +413,7 @@ namespace sibs
void SibsConfig::finished()
{
if((int)packageType == -1)
- throw ParserException("Missing required config package.type. Expected to be one either 'executable' or 'library'");
+ throw ParserException("Missing required config package.type. Expected to be one either 'executable', 'static', 'dynamic' or 'library'");
finishedProcessing = true;
}
} \ No newline at end of file
diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp
index 9a15938..899a1af 100644
--- a/src/FileUtil.cpp
+++ b/src/FileUtil.cpp
@@ -125,6 +125,7 @@ namespace sibs
errMsg += strerror(error);
return Result<bool>::Err(errMsg);
}
+ setbuf(file, NULL);
fwrite(data.data, 1, data.size, file);
fclose(file);
return Result<bool>::Ok(true);
diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp
index 177f1b9..203cd0f 100644
--- a/src/GlobalLib.cpp
+++ b/src/GlobalLib.cpp
@@ -136,11 +136,19 @@ namespace sibs
if(ninja.getSourceFiles().empty())
{
- return Result<string>::Ok("");
+ return Result<string>::Ok("No source files in dependency (header only library?)");
}
else
{
- string debugBuildPath = packageDir + "/build/debug";
+ string debugBuildPath = packageDir + "/sibs-build/debug";
+ string staticLibPath = debugBuildPath;
+ staticLibPath += "/lib";
+ staticLibPath += name;
+ staticLibPath += ".a";
+ linkerFlagCallbackFunc(staticLibPath);
+
+ // TODO: Use different directories depending on the project type, but .o build files should be in the same directory
+ // no matter what project type, since they are used for executables, static/dynamic libraries
Result<bool> createBuildDirResult = createDirectoryRecursive(debugBuildPath.c_str());
if(createBuildDirResult.isErr())
return Result<string>::Err(createBuildDirResult);
@@ -153,11 +161,6 @@ namespace sibs
if (buildResult.isErr())
return Result<string>::Err(buildResult.getErrMsg());
- string staticLibPath = debugBuildPath;
- staticLibPath += "/lib";
- staticLibPath += name;
- staticLibPath += ".a";
- linkerFlagCallbackFunc(staticLibPath);
return Result<string>::Ok(staticLibPath);
}
}
diff --git a/src/main.cpp b/src/main.cpp
index 5c25414..9d6c88e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,9 @@
#include <cstdio>
+#include <iostream>
+#include <unordered_set>
#include "../include/FileUtil.hpp"
#include "../include/Conf.hpp"
+#include "../include/Exec.hpp"
#include "../backend/ninja/Ninja.hpp"
using namespace std;
@@ -9,17 +12,33 @@ using namespace sibs;
// TODO: Fail if multiple versions of the same dependency is used
// as linking will fail because of multiple definitions of the same thing
-// TODO: Detect recursive dependencies and give error.
+// TODO: Detect circular dependencies
+
+// TODO: Prevent infinite recursion in source file searching when there are symlinks.
+// Either do not follow the symlinks or use a hash map with every searched directory
+// to only go inside a directory once
void usage()
{
- printf("usage: sibs COMMAND\n\n");
+ printf("Usage: sibs COMMAND\n\n");
printf("Simple Build System for Native Languages\n\n");
- printf("Options:\n");
- printf(" -p\t\tPath to project (directory that contains main project.conf file)\n");
- printf(" -h\t\tPrint this help menu\n\n");
printf("Commands:\n");
printf(" build\t\tBuild a project that contains a project.conf file\n");
+ printf(" new\t\tCreate a new project\n");
+ exit(1);
+}
+
+void usageNew()
+{
+ printf("Usage: sibs new <project_name> <--exec|--static|--dynamic>\n\n");
+ printf("Create a new sibs project\n\n");
+ printf("Options:\n");
+ printf(" project_name\t\tThe name of the project you want to create\n");
+ printf(" --exec\t\t\tProject compiles to an executable\n");
+ printf(" --static\t\t\tProject compiles to a static library\n");
+ printf(" --dynamic\t\t\tProject compiles to a dynamic library\n");
+ printf("Examples:\n");
+ printf(" sibs new hello_world --exec\n");
exit(1);
}
@@ -28,12 +47,14 @@ void validateDirectoryPath(const char *projectPath)
FileType projectPathFileType = getFileType(projectPath);
if(projectPathFileType == FileType::FILE_NOT_FOUND)
{
- perror(projectPath);
+ string errMsg = "Invalid project path: ";
+ errMsg += projectPath;
+ perror(errMsg.c_str());
exit(2);
}
else if(projectPathFileType == FileType::REGULAR)
{
- printf("Expected project path (%s) to be a directory, was a file", projectPath);
+ cerr <<"Expected project path (" << projectPath << ") to be a directory, was a file" << endl;
exit(3);
}
}
@@ -43,17 +64,19 @@ void validateFilePath(const char *projectConfPath)
FileType projectConfFileType = getFileType(projectConfPath);
if(projectConfFileType == FileType::FILE_NOT_FOUND)
{
- perror(projectConfPath);
+ string errMsg = "Invalid project.conf path: ";
+ errMsg += projectConfPath;
+ perror(errMsg.c_str());
exit(4);
}
else if(projectConfFileType == FileType::DIRECTORY)
{
- printf("Expected project path (%s) to be a file, was a directory", projectConfPath);
+ cerr << "Expected project path (" << projectConfPath << ") to be a file, was a directory" << endl;
exit(5);
}
}
-const char *sourceFileExtensions[] = { "cc", "cpp", "cxx" };
+const char *sourceFileExtensions[] = { "c", "cc", "cpp", "cxx" };
bool isSourceFile(tinydir_file *file)
{
if(!file->is_reg)
@@ -68,8 +91,20 @@ bool isSourceFile(tinydir_file *file)
return false;
}
-void build(string projectPath)
+int buildProject(int argc, const char **argv)
{
+ if(argc > 1)
+ {
+ cerr << "Expected 'build' command to only be followed by one argument which is the path to a directory that contains a project.conf file, ";
+ cerr << "or none; in which case build would use the working directory as target directory" << endl << endl;
+ usage();
+ }
+
+ // TODO: If argc == 0 and working directory does not contain project.conf, then search every parent directory until one is found
+ string projectPath = ".";
+ if(argc == 1)
+ projectPath = argv[0];
+
validateDirectoryPath(projectPath.c_str());
if(projectPath.back() != '/')
projectPath += "/";
@@ -82,20 +117,36 @@ void build(string projectPath)
Result<bool> result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig);
if(result.isErr())
{
- printf("Failed to read config: %s\n", result.getErrMsg().c_str());
+ cerr << "Failed to read config: " << result.getErrMsg() << endl;
exit(6);
}
if(sibsConfig.getPackageName().empty())
{
- printf("project.conf is missing required field package.name\n");
+ cerr << "project.conf is missing required field package.name" << endl;
exit(10);
}
//string projectSrcPath = projectPath + "/src";
//validateDirectoryPath(projectSrcPath.c_str());
- backend::Ninja ninja;
+ PackageType packageType = sibsConfig.getPackageType();
+ backend::Ninja::LibraryType libraryType;
+ switch(packageType)
+ {
+ case PackageType::EXECUTABLE:
+ libraryType = backend::Ninja::LibraryType::EXECUTABLE;
+ break;
+ case PackageType::STATIC:
+ libraryType = backend::Ninja::LibraryType::STATIC;
+ break;
+ case PackageType::DYNAMIC:
+ case PackageType::LIBRARY:
+ libraryType = backend::Ninja::LibraryType::DYNAMIC;
+ break;
+ }
+
+ backend::Ninja ninja(libraryType);
walkDirFilesRecursive(projectPath.c_str(), [&ninja, &projectPath](tinydir_file *file)
{
if (isSourceFile(file))
@@ -108,11 +159,11 @@ void build(string projectPath)
}
});
- string debugBuildPath = projectPath + "/build/debug";
+ string debugBuildPath = projectPath + "/sibs-build/debug";
Result<bool> buildFileResult = ninja.createBuildFile(sibsConfig.getPackageName(), sibsConfig.getDependencies(), debugBuildPath.c_str());
if(buildFileResult.isErr())
{
- printf("Failed to build ninja file: %s\n", buildFileResult.getErrMsg().c_str());
+ cerr << "Failed to build ninja file: " << buildFileResult.getErrMsg() << endl;
exit(7);
}
@@ -121,81 +172,144 @@ void build(string projectPath)
{
exit(8);
}
+
+ return 0;
}
-int main(int argc, const char **argv)
+void newProjectCreateMainDir(const string &projectPath)
{
- string projectPath;
- string command;
- for(int i = 1; i < argc; ++i)
+ Result<bool> createProjectDirResult = createDirectoryRecursive(projectPath.c_str());
+ if(createProjectDirResult.isErr())
{
- const char *arg = argv[i];
- if(arg[0] == '-')
- {
- // Option
- if(strcmp(arg, "-p") == 0)
- {
- if(!projectPath.empty())
- {
- printf("-p option defined twice, should only be defined once\n");
- exit(1);
- }
-
- if(i < argc - 1)
- {
- projectPath = argv[i + 1];
- ++i;
- }
- else
- {
- printf("Expected project path after option -p\n");
- exit(1);
- }
- }
- else if(strcmp(arg, "-h") == 0)
- {
- usage();
- }
- else
- {
- printf("Invalid option %s, type sibs -h to see valid options\n", arg);
- exit(1);
- }
- }
- else
- {
- if(!command.empty())
- {
- printf("Found command twice. First as %s, then as %s\n", command.c_str(), arg);
- exit(1);
- }
- command = arg;
- }
+ cerr << "Failed to create project main directory: " << createProjectDirResult.getErrMsg() << endl;
+ exit(20);
}
+}
- if(projectPath.empty())
+void createProjectSubDir(const string &dir)
+{
+ Result<bool> createProjectDirResult = createDirectoryRecursive(dir.c_str());
+ if(createProjectDirResult.isErr())
{
- Result<string> projectPathResult = getCwd();
- if(projectPathResult.isErr())
- {
- printf("%s\n", projectPathResult.getErrMsg().c_str());
- exit(11);
- }
- projectPath = projectPathResult.unwrap();
+ cerr << "Failed to create directory in project: " << createProjectDirResult.getErrMsg() << endl;
+ exit(20);
}
+}
- if(command == "build")
- build(projectPath);
- else if(command.empty())
+void createProjectFile(const string &projectFilePath, const string &fileContent)
+{
+ Result<bool> fileOverwriteResult = fileOverwrite(projectFilePath.c_str(), fileContent.c_str());
+ if(fileOverwriteResult.isErr())
{
- printf("No command provided, type sibs -h to see valid commands\n");
- exit(1);
+ cerr << "Failed to create project file: " << fileOverwriteResult.getErrMsg() << endl;
+ exit(20);
}
+}
+
+void newProjectCreateConf(const string &projectName, const string &projectType, const string &projectPath)
+{
+ string projectConfStr = "[package]\n";
+ projectConfStr += "name = \"" + projectName + "\"\n";
+ projectConfStr += "type = \"" + projectType + "\"\n";
+ projectConfStr += "version = \"0.1.0\"\n\n";
+ projectConfStr += "[dependencies]\n";
+
+ string projectConfPath = projectPath;
+ projectConfPath += "/project.conf";
+ Result<bool> fileOverwriteResult = fileOverwrite(projectConfPath.c_str(), projectConfStr.c_str());
+ if(fileOverwriteResult.isErr())
+ {
+ cerr << "Failed to create project.conf: " << fileOverwriteResult.getErrMsg() << endl;
+ exit(20);
+ }
+}
+
+// This can be replaced with createDirectory and fileOverwrite, but it's not important
+// so there is no reason to do it (right now)
+Result<ExecResult> gitInitProject(const string &projectPath)
+{
+ string cmd = "git init '";
+ cmd += projectPath;
+ cmd += "'";
+ return exec(cmd.c_str());
+}
+
+int newProject(int argc, const char **argv)
+{
+ if(argc != 2)
+ {
+ cerr << "Expected 'new' command to be followed by two arguments - project name and type of project (--exec, --static or --dynamic)" << endl << endl;
+ usageNew();
+ }
+
+ Result<string> cwdResult = getCwd();
+ if(cwdResult.isErr())
+ {
+ cerr << "Failed to get current working directory: " << cwdResult.getErrMsg() << endl;
+ exit(20);
+ }
+
+ string projectName = argv[0];
+ string projectPath = cwdResult.unwrap();
+ projectPath += "/";
+ projectPath += projectName;
+ bool projectPathExists = getFileType(projectPath.c_str()) != FileType::FILE_NOT_FOUND;
+ if(projectPathExists)
+ {
+ cerr << "Unable to create a new project at path '" << projectPath << "'. A file or directory already exists in the same location" << endl;
+ exit(20);
+ }
+
+ const char *projectType = argv[1];
+
+ string projectTypeConf;
+ if(strcmp(projectType, "--exec") == 0)
+ projectTypeConf = "executable";
+ else if(strcmp(projectType, "--static") == 0)
+ projectTypeConf = "static";
+ else if(strcmp(projectType, "--dynamic") == 0)
+ projectTypeConf = "dynamic";
else
{
- printf("Invalid command %s, expected: build\n", command.c_str());
- exit(1);
+ cerr << "Expected project type to be either --exec, --static or --dynamic; was: " << projectType << endl << endl;
+ usageNew();
+ }
+
+ newProjectCreateMainDir(projectPath);
+ newProjectCreateConf(projectName, projectTypeConf, projectPath);
+ createProjectSubDir(projectPath + "/src");
+ createProjectSubDir(projectPath + "/include");
+ createProjectFile(projectPath + "/src/main.cpp", "#include <cstdio>\n\nint main()\n{ return 0;\n}\n");
+ // We are ignoring git init result on purpose. If it fails, just ignore it; not important
+ gitInitProject(projectPath);
+ return 0;
+}
+
+int main(int argc, const char **argv)
+{
+ unordered_map<string, string> param;
+ unordered_set<string> flags;
+
+ for(int i = 1; i < argc; ++i)
+ {
+ const char *arg = argv[i];
+ int subCommandArgCount = argc - i - 1;
+ const char **subCommandArgPtr = argv + i + 1;
+ if(strcmp(arg, "build") == 0)
+ {
+ return buildProject(subCommandArgCount, subCommandArgPtr);
+ }
+ else if(strcmp(arg, "new") == 0)
+ {
+ return newProject(subCommandArgCount, subCommandArgPtr);
+ }
+ else
+ {
+ cerr << "Expected command to be either 'build' or 'new', was: " << arg << endl << endl;
+ usage();
+ }
}
+ usage();
return 0;
}