aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/FileUtil.hpp3
-rw-r--r--src/Conf.cpp4
-rw-r--r--src/FileUtil.cpp12
-rw-r--r--src/main.cpp260
4 files changed, 242 insertions, 37 deletions
diff --git a/include/FileUtil.hpp b/include/FileUtil.hpp
index 7288426..b808573 100644
--- a/include/FileUtil.hpp
+++ b/include/FileUtil.hpp
@@ -21,7 +21,7 @@ namespace sibs
using FileString = std::basic_string<_tinydir_char_t, std::char_traits<_tinydir_char_t>, std::allocator<_tinydir_char_t>>;
#if OS_FAMILY == OS_FAMILY_POSIX
-#define toUtf8(input) input
+ #define toUtf8(input) input
FileString toFileString(const std::string &utf8Str);
FileString toFileString(const StringView &utf8Str);
#else
@@ -49,6 +49,7 @@ namespace sibs
void walkDirFiles(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
void walkDirFilesRecursive(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
Result<StringView> getFileContent(const _tinydir_char_t *filepath);
+ Result<bool> fileWrite(const _tinydir_char_t *filepath, StringView data);
Result<bool> fileOverwrite(const _tinydir_char_t *filepath, StringView data);
Result<FileString> getHomeDir();
Result<FileString> getCwd();
diff --git a/src/Conf.cpp b/src/Conf.cpp
index 883022b..4424551 100644
--- a/src/Conf.cpp
+++ b/src/Conf.cpp
@@ -570,12 +570,16 @@ namespace sibs
bool isProjectNameValid(const string &projectName)
{
+ if(projectName.empty())
+ return false;
+
for(int i = 0; i < projectName.size(); ++i)
{
char c = projectName[i];
if(!isalpha(c) && !isdigit(c) && c != '-' && c != '_')
return false;
}
+
return true;
}
diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp
index 53c1132..aca2778 100644
--- a/src/FileUtil.cpp
+++ b/src/FileUtil.cpp
@@ -229,6 +229,18 @@ namespace sibs
fclose(file);
return Result<StringView>::Ok(StringView(result, fileSize));
}
+
+ Result<bool> fileWrite(const _tinydir_char_t *filepath, StringView data)
+ {
+ if(getFileType(filepath) != FileType::FILE_NOT_FOUND)
+ {
+ string errMsg = "Failed to write to file: ";
+ errMsg += toUtf8(filepath);
+ errMsg += "; reason: file already exists";
+ return Result<bool>::Err(errMsg);
+ }
+ return fileOverwrite(filepath, data);
+ }
Result<bool> fileOverwrite(const _tinydir_char_t *filepath, StringView data)
{
diff --git a/src/main.cpp b/src/main.cpp
index a3f09a8..aabf83e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -90,8 +90,10 @@ using namespace std::chrono;
// TODO: Add program command for generating compile_commands.json without compiling code, without using Ninja
#if OS_FAMILY == OS_FAMILY_POSIX
+#define fout std::cout
#define ferr std::cerr
#else
+#define fout std::wcout
#define ferr std::wcerr
#endif
@@ -111,7 +113,7 @@ void usageBuild()
printf("Usage: sibs build [project_path] [--debug|--release] [--sanitize]\n\n");
printf("Build a sibs project\n\n");
printf("Options:\n");
- printf(" project_path\t\tThe directory containing a project.conf file - Optional (default: current working directory)\n");
+ printf(" project_path\t\tThe directory containing a project.conf file - Optional (default: current directory)\n");
printf(" --debug|--release\t\tOptimization level to build project and dependencies with (if not a system package) - Optional (default: --debug)\n");
printf(" --sanitize\t\tAdd runtime address/undefined behavior sanitization. Program can be up to 3 times slower and use 10 times as much RAM. Ignored if compiler doesn't support sanitization - Optional (default: disabled)\n");
printf("Examples:\n");
@@ -142,7 +144,7 @@ void usageTest()
printf("Usage: sibs test [project_path] [--no-sanitize]\n\n");
printf("Build and run tests for a sibs project\n\n");
printf("Options:\n");
- printf(" project_path\t\tThe directory containing a project.conf file - Optional (default: current working directory)\n");
+ printf(" project_path\t\tThe directory containing a project.conf file - Optional (default: current directory)\n");
printf(" --no-sanitize\t\tDisable runtime address/undefined behavior - Optional\n");
printf("Examples:\n");
printf(" sibs test\n");
@@ -150,6 +152,21 @@ void usageTest()
exit(1);
}
+void usageInit()
+{
+ printf("Usage: sibs init [project_path] <--exec|--static|--dynamic>\n\n");
+ printf("Create sibs project structure in an existing directory\n\n");
+ printf("Options:\n");
+ printf(" project_path\t\tThe directory where you want to initialize sibs project - Optional (default: current directory)\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 init --exec\n");
+ printf(" sibs init dirA/dirB --dynamic");
+ exit(1);
+}
+
void validateDirectoryPath(const _tinydir_char_t *projectPath)
{
FileType projectPathFileType = getFileType(projectPath);
@@ -369,7 +386,7 @@ int buildProject(int argc, const _tinydir_char_t **argv)
int testProject(int argc, const _tinydir_char_t **argv)
{
- if(argc > 1)
+ if(argc > 2)
usageTest();
FileString projectPath;
@@ -431,37 +448,18 @@ int testProject(int argc, const _tinydir_char_t **argv)
return buildProject(projectPath, projectConfFilePath, sibsConfig);
}
-void newProjectCreateMainDir(const FileString &projectPath)
+// Returns nullptr if @charToFind is not found
+const _tinydir_char_t* findLastOf(const _tinydir_char_t *str, const int strSize, const char charToFind)
{
- Result<bool> createProjectDirResult = createDirectoryRecursive(projectPath.c_str());
- if(createProjectDirResult.isErr())
+ for(int i = strSize; i >= 0; --i)
{
- ferr << "Failed to create project main directory: " << toFileString(createProjectDirResult.getErrMsg()) << endl;
- exit(20);
+ if(str[i] == charToFind)
+ return str + i;
}
+ return nullptr;
}
-void createProjectSubDir(const FileString &dir)
-{
- Result<bool> createProjectDirResult = createDirectoryRecursive(dir.c_str());
- if(createProjectDirResult.isErr())
- {
- ferr << "Failed to create directory in project: " << toFileString(createProjectDirResult.getErrMsg()) << endl;
- exit(20);
- }
-}
-
-void createProjectFile(const FileString &projectFilePath, const string &fileContent)
-{
- Result<bool> fileOverwriteResult = fileOverwrite(projectFilePath.c_str(), fileContent.c_str());
- if(fileOverwriteResult.isErr())
- {
- ferr << "Failed to create project file: " << toFileString(fileOverwriteResult.getErrMsg()) << endl;
- exit(20);
- }
-}
-
-void newProjectCreateConf(const string &projectName, const string &projectType, const FileString &projectPath)
+Result<bool> newProjectCreateConf(const string &projectName, const string &projectType, const FileString &projectPath)
{
string projectConfStr = "[package]\n";
projectConfStr += "name = \"" + projectName + "\"\n";
@@ -476,10 +474,20 @@ void newProjectCreateConf(const string &projectName, const string &projectType,
FileString projectConfPath = projectPath;
projectConfPath += TINYDIR_STRING("/project.conf");
- Result<bool> fileOverwriteResult = fileOverwrite(projectConfPath.c_str(), projectConfStr.c_str());
+ return fileWrite(projectConfPath.c_str(), projectConfStr.c_str());
+}
+
+Result<bool> createDirectoryRecursive(const FileString &dir)
+{
+ return createDirectoryRecursive(dir.c_str());
+}
+
+void createProjectFile(const FileString &projectFilePath, const string &fileContent)
+{
+ Result<bool> fileOverwriteResult = fileOverwrite(projectFilePath.c_str(), fileContent.c_str());
if(fileOverwriteResult.isErr())
{
- ferr << "Failed to create project.conf: " << toFileString(fileOverwriteResult.getErrMsg()) << endl;
+ ferr << "Failed to create project file: " << toFileString(fileOverwriteResult.getErrMsg()) << endl;
exit(20);
}
}
@@ -494,6 +502,169 @@ Result<ExecResult> gitInitProject(const FileString &projectPath)
return exec(cmd.c_str());
}
+int initProject(int argc, const _tinydir_char_t **argv)
+{
+ if(argc > 2)
+ usageInit();
+
+ FileString projectPath;
+ const _tinydir_char_t *projectType = nullptr;
+
+ for(int i = 0; i < argc; ++i)
+ {
+ const _tinydir_char_t *arg = argv[i];
+ if(_tinydir_strcmp(arg, TINYDIR_STRING("--exec")) == 0)
+ {
+ if(projectType)
+ {
+ ferr << "Error: Project type was defined more than once. First as " << projectType << " then as " << arg << endl;
+ usageInit();
+ }
+ projectType = arg;
+ }
+ else if(_tinydir_strcmp(arg, TINYDIR_STRING("--static")) == 0)
+ {
+ if(projectType)
+ {
+ ferr << "Error: Project type was defined more than once. First as " << projectType << " then as " << arg << endl;
+ usageInit();
+ }
+ projectType = arg;
+ }
+ else if(_tinydir_strcmp(arg, TINYDIR_STRING("--dynamic")) == 0)
+ {
+ if(projectType)
+ {
+ ferr << "Error: Project type was defined more than once. First as " << projectType << " then as " << arg << endl;
+ usageInit();
+ }
+ projectType = arg;
+ }
+ else if(_tinydir_strncmp(arg, TINYDIR_STRING("--"), 2) == 0)
+ {
+ ferr << "Error: Invalid argument " << arg << endl;
+ usageInit();
+ }
+ else
+ {
+ if(!projectPath.empty())
+ {
+ ferr << "Error: Project path was defined more than once. First defined as " << projectPath << " then as " << arg << endl;
+ usageInit();
+ }
+ projectPath = arg;
+ }
+ }
+
+ if(!projectType)
+ {
+ ferr << "Error: Project type not defined, expected to be either --exec, --static or --dynamic" << endl;
+ usageInit();
+ }
+
+ string projectTypeConf;
+ if(_tinydir_strcmp(projectType, TINYDIR_STRING("--exec")) == 0)
+ projectTypeConf = "executable";
+ else if(_tinydir_strcmp(projectType, TINYDIR_STRING("--static")) == 0)
+ projectTypeConf = "static";
+ else if(_tinydir_strcmp(projectType, TINYDIR_STRING("--dynamic")) == 0)
+ projectTypeConf = "dynamic";
+ else
+ {
+ ferr << "Expected project type to be either --exec, --static or --dynamic; was: " << projectType << endl << endl;
+ usageInit();
+ }
+
+ // TODO: If projectPath is not defined and working directory does not contain project.conf, then search every parent directory until one is found
+ if(projectPath.empty())
+ projectPath = TINYDIR_STRING(".");
+
+ validateDirectoryPath(projectPath.c_str());
+ if(projectPath.back() != '/')
+ projectPath += TINYDIR_STRING("/");
+
+ Result<FileString> projectRealPathResult = getRealPath(projectPath.c_str());
+ if(!projectRealPathResult)
+ {
+ ferr << "Failed to get real path for: '" << projectPath.c_str() << "': " << toFileString(projectRealPathResult.getErrMsg()) << endl;
+ exit(40);
+ }
+ projectPath = projectRealPathResult.unwrap();
+
+ FileType projectFileType = getFileType(projectPath.c_str());
+ if(projectFileType == FileType::FILE_NOT_FOUND)
+ {
+ ferr << "Directory not found: '" << projectPath << "', unable to initialize project" << endl;
+ exit(20);
+ }
+ else if(projectFileType == FileType::REGULAR)
+ {
+ ferr << "Expected project path : '" << projectPath << "' to be a directory, was a file. Unable to initialize project" << endl;
+ exit(21);
+ }
+
+ const _tinydir_char_t *projectNameForwardSlash = findLastOf(projectPath.c_str(), projectPath.size(), '/');
+ const _tinydir_char_t *projectNameBackwardSlash = findLastOf(projectPath.c_str(), projectPath.size(), '\\');
+ string projectName;
+ if(projectNameForwardSlash && projectNameBackwardSlash)
+ {
+ if(projectNameForwardSlash > projectNameBackwardSlash)
+ projectName = toUtf8(projectNameForwardSlash + 1);
+ else
+ projectName = toUtf8(projectNameBackwardSlash + 1);
+ }
+ else if(!projectNameForwardSlash && projectNameBackwardSlash)
+ projectName = toUtf8(projectNameBackwardSlash + 1);
+ else if(!projectNameBackwardSlash && projectNameForwardSlash)
+ projectName = toUtf8(projectNameForwardSlash + 1);
+ else
+ projectName = toUtf8(projectPath);
+
+ if(!isProjectNameValid(projectName))
+ {
+ ferr << "Project name can only contain alphanumerical characters, dash (-) or underscore (_) and has to be longer than 0 characters" << endl;
+ exit(20);
+ }
+
+ auto createProjectConfResult = newProjectCreateConf(projectName, projectTypeConf, projectPath);
+ if(!createProjectConfResult)
+ {
+ ferr << "A project already exists in the directory " << projectPath << ". Error: failed to create project.conf, reason: " << createProjectConfResult.getErrMsg() << endl;
+ exit(20);
+ }
+ createDirectoryRecursive(projectPath + TINYDIR_STRING("/src"));
+ createDirectoryRecursive(projectPath + TINYDIR_STRING("/include"));
+ createDirectoryRecursive(projectPath + TINYDIR_STRING("/tests"));
+ if(projectTypeConf == "executable")
+ {
+ auto mainFilePath = projectPath + TINYDIR_STRING("/src/main.cpp");
+ Result<bool> fileOverwriteResult = fileWrite(mainFilePath.c_str(), "#include <cstdio>\n\nint main(int argc, char **argv)\n{\n printf(\"hello, world!\\n\");\n return 0;\n}\n");
+ if(!fileOverwriteResult)
+ fout << "Warning: Failed to create project file: " << toFileString(fileOverwriteResult.getErrMsg()) << endl;
+ }
+ gitInitProject(projectPath);
+ return 0;
+}
+
+void newProjectCreateMainDir(const FileString &projectPath)
+{
+ Result<bool> createProjectDirResult = createDirectoryRecursive(projectPath.c_str());
+ if(createProjectDirResult.isErr())
+ {
+ ferr << "Failed to create project main directory: " << toFileString(createProjectDirResult.getErrMsg()) << endl;
+ exit(20);
+ }
+}
+
+void checkFailCreateSubDir(Result<bool> createSubDirResult)
+{
+ if(!createSubDirResult)
+ {
+ ferr << "Failed to create directory in project: " << toFileString(createSubDirResult.getErrMsg()) << endl;
+ exit(20);
+ }
+}
+
int newProject(int argc, const _tinydir_char_t **argv)
{
if(argc != 2)
@@ -542,12 +713,25 @@ int newProject(int argc, const _tinydir_char_t **argv)
}
newProjectCreateMainDir(projectPath);
- newProjectCreateConf(projectName, projectTypeConf, projectPath);
- createProjectSubDir(projectPath + TINYDIR_STRING("/src"));
- createProjectSubDir(projectPath + TINYDIR_STRING("/include"));
- createProjectSubDir(projectPath + TINYDIR_STRING("/tests"));
+ auto createProjectConfResult = newProjectCreateConf(projectName, projectTypeConf, projectPath);
+ if(!createProjectConfResult)
+ {
+ ferr << "Failed to create project.conf: " << toFileString(createProjectConfResult.getErrMsg()) << endl;
+ exit(20);
+ }
+ createDirectoryRecursive(projectPath + TINYDIR_STRING("/src"));
+ createDirectoryRecursive(projectPath + TINYDIR_STRING("/include"));
+ createDirectoryRecursive(projectPath + TINYDIR_STRING("/tests"));
if(projectTypeConf == "executable")
- createProjectFile(projectPath + TINYDIR_STRING("/src/main.cpp"), "#include <cstdio>\n\nint main(int argc, char **argv)\n{\n printf(\"hello, world!\\n\");\n return 0;\n}\n");
+ {
+ auto mainFilePath = projectPath + TINYDIR_STRING("/src/main.cpp");
+ Result<bool> fileOverwriteResult = fileWrite(mainFilePath.c_str(), "#include <cstdio>\n\nint main(int argc, char **argv)\n{\n printf(\"hello, world!\\n\");\n return 0;\n}\n");
+ if(!fileOverwriteResult)
+ {
+ ferr << "Failed to create project file: " << toFileString(fileOverwriteResult.getErrMsg()) << endl;
+ exit(20);
+ }
+ }
// We are ignoring git init result on purpose. If it fails, just ignore it; not important
gitInitProject(projectPath);
return 0;
@@ -579,6 +763,10 @@ int wmain(int argc, const _tinydir_char_t **argv)
{
return testProject(subCommandArgCount, subCommandArgPtr);
}
+ else if(_tinydir_strcmp(arg, TINYDIR_STRING("init")) == 0)
+ {
+ return initProject(subCommandArgCount, subCommandArgPtr);
+ }
else
{
ferr << "Expected command to be either 'build', 'new' or 'test', was: " << arg << endl << endl;