/******************************************************************************
* File - main . cpp
* Author - Joey Pollack
* Date - 2021 / 12 / 03 ( y / m / d )
* Mod Date - 2021 / 12 / 03 ( y / m / d )
* Description - Generates a new C + + project with CMake
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// TODO: Generate a clean.sh script for linux
# include <utils/CmdArgParser.h>
# include <utils/StringManip.h>
# include <iostream>
# include <filesystem>
# include <string>
# include <fstream>
# include <ctime>
typedef jpUtils : : CmdArgParser : : ArgDesc ArgDesc ;
int main ( int argc , char * * argv )
{
jpUtils : : CmdArgParser argParser ( argc , argv , " Generates a new C++ project with CMake. " ) ;
// Required args
argParser . AddArg ( ArgDesc ( " ProjectName " , false , 1 , " The name of the project to generate. " ) ) ;
// Options
argParser . AddArg ( ArgDesc ( " -h " , true , 0 , " Displays this help messasge. " ) ) ;
argParser . AddArg ( ArgDesc ( " -v " , true , 0 , " Verbose - Print extra info while running. " ) ) ;
argParser . AddArg ( ArgDesc ( " -p " , true , 1 , " Platform - Can be one of: win, linux, both. Default is win. " ) ) ;
argParser . AddArg ( ArgDesc ( " -l " , true , 1 , " License - Generate an MIT license file with the given name. " ) ) ;
argParser . AddArg ( ArgDesc ( " -d " , true , 1 , " Path to the location to generate the project at. Defaults to the current dirctory. " ) ) ;
argParser . AddArg ( ArgDesc ( " -i " , true , 0 , " Generate a .gitignore file " ) ) ;
argParser . AddArg ( ArgDesc ( " -m " , true , 1 , " Specify the CMake version to require. Default is 3.16.3 " ) ) ;
argParser . AddArg ( ArgDesc ( " -c " , true , 1 , " Specify the C++ version to require. Default is 17 " ) ) ;
bool result = argParser . Parse ( ) ;
if ( ! result )
{
if ( argParser . ContainsOption ( " -h " ) )
{
std : : cout < < argParser . GetUsageText ( ) < < std : : endl ;
return 0 ;
}
else
{
std : : cout < < argParser . GetErrorMessage ( ) ;
std : : cout < < " \n " < < argParser . GetUsageText ( ) < < std : : endl ;
return 1 ;
}
}
////////////////////////////////////////////////////////////
// Generate directory structure
////////////////////////////////////////////////////////////
std : : filesystem : : path root = std : : filesystem : : current_path ( ) ;
if ( argParser . ContainsOption ( " -d " ) )
{
if ( std : : filesystem : : exists ( argParser . GetOptionValue ( " -d " ) . values [ 0 ] ) )
{
root = argParser . GetOptionValue ( " -d " ) . values [ 0 ] ;
}
}
std : : cout < < " \n Generating project at location: " < < root . string ( ) . c_str ( ) ;
std : : string project_name = argParser . GetPositionalArg ( 0 ) . values [ 0 ] ;
root / = std : : filesystem : : path ( project_name ) ;
if ( ! std : : filesystem : : create_directory ( root ) )
{
std : : cout < < " \n Could not create directory: " < < root . string ( ) . c_str ( ) ;
return 1 ;
}
if ( ! std : : filesystem : : create_directory ( root / " build " ) )
{
std : : cout < < " \n Could not create build directory " ;
return 1 ;
}
if ( ! std : : filesystem : : create_directory ( root / " scripts " ) )
{
std : : cout < < " \n Could not create scripts directory " ;
return 1 ;
}
if ( ! std : : filesystem : : create_directory ( root / " src " ) )
{
std : : cout < < " \n Could not create src directory " ;
return 1 ;
}
////////////////////////////////////////////////////////////
// Generate CMake files
////////////////////////////////////////////////////////////
std : : filesystem : : path cmake_file = root / std : : filesystem : : path ( " CMakeLists.txt " ) ;
std : : ofstream ofs ( cmake_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the CMake file: " < < cmake_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating CMake file: " < < cmake_file . string ( ) . c_str ( ) ;
std : : string cmversion = " 3.16.3 " ;
if ( argParser . ContainsOption ( " -m " ) )
{
cmversion = argParser . GetOptionValue ( " -m " ) . values [ 0 ] ;
}
std : : string cversion = " 17 " ;
if ( argParser . ContainsOption ( " -c " ) )
{
cversion = argParser . GetOptionValue ( " -c " ) . values [ 0 ] ;
}
ofs < < " cmake_minimum_required(VERSION " < < cmversion < < " ) " ;
ofs < < " \n \n # set the project name and version " ;
ofs < < " \n project( " < < project_name < < " VERSION 0.1.0) " ;
ofs < < " \n \n # specify the C++ standard " ;
ofs < < " \n set(CMAKE_CXX_STANDARD " < < cversion < < " ) " ;
ofs < < " \n set(CMAKE_CXX_STANDARD_REQUIRED True) " ;
ofs < < " \n \n configure_file( " < < project_name < < " Config.h.in " < < project_name < < " Config.h) " ;
ofs < < " \n \n # specify the project source files " ;
ofs < < " \n set( " < < jpUtils : : StringManip : : ToUpper ( project_name ) < < " _SRC " ;
ofs < < " \n \t src/main.cpp " ;
ofs < < " \n ) " ;
ofs < < " \n \n # add the executable " ;
ofs < < " \n add_executable(${PROJECT_NAME} ${ " < < jpUtils : : StringManip : : ToUpper ( project_name ) < < " _SRC}) " ;
ofs < < " \n \n target_include_directories(${PROJECT_NAME} " ;
ofs < < " \n \t PUBLIC \" ${PROJECT_BINARY_DIR} \" " ;
ofs < < " \n \t PUBLIC src " ;
ofs < < " \n ) " ;
ofs . close ( ) ;
ofs . clear ( ) ;
////////////////////////////////////////////////////////////
// Generate Config file
////////////////////////////////////////////////////////////
std : : string config_name = project_name ;
config_name + = " Config.h.in " ;
std : : filesystem : : path config_file = root / config_name ;
ofs = std : : ofstream ( config_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the Config file: " < < config_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating Config file: " < < config_file . string ( ) . c_str ( ) ;
ofs < < " #define " < < project_name < < " _VERSION_MAJOR @ " < < project_name < < " _VERSION_MAJOR@ " ;
ofs < < " \n #define " < < project_name < < " _VERSION_MINOR @ " < < project_name < < " _VERSION_MINOR@ " ;
ofs < < " \n #define " < < project_name < < " _VERSION_PATCH @ " < < project_name < < " _VERSION_PATCH@ " ;
ofs . close ( ) ;
ofs . clear ( ) ;
////////////////////////////////////////////////////////////
// Generate License file
////////////////////////////////////////////////////////////
if ( argParser . ContainsOption ( " -l " ) )
{
std : : filesystem : : path license_file = root / " LICENSE " ;
ofs = std : : ofstream ( license_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the License file: " < < license_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating License file: " < < license_file . string ( ) . c_str ( ) ;
time_t now = time ( 0 ) ;
tm * gmtm = gmtime ( & now ) ;
ofs < < " Copyright " < < gmtm - > tm_year + 1900 < < " " < < argParser . GetOptionValue ( " -l " ) . values [ 0 ] ;
ofs < < " \n \n Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \" Software \" ), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: " ;
ofs < < " \n \n The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. " ;
ofs < < " \n \n THE SOFTWARE IS PROVIDED \" AS IS \" , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. " ;
ofs . close ( ) ;
ofs . clear ( ) ;
}
////////////////////////////////////////////////////////////
// Generate .gitignore file
////////////////////////////////////////////////////////////
if ( argParser . ContainsOption ( " -i " ) )
{
std : : filesystem : : path ignore_file = root / " .gitignore " ;
ofs = std : : ofstream ( ignore_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the .gitignore file: " < < ignore_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating .gitignore file: " < < ignore_file . string ( ) . c_str ( ) ;
ofs < < " ######################## VSCODE IGNORES " ;
ofs < < " \n .vscode/ " ;
ofs < < " \n " ;
ofs < < " \n " ;
ofs < < " \n ######################## C++ IGNORES " ;
ofs < < " \n # Prerequisites " ;
ofs < < " \n *.d " ;
ofs < < " \n " ;
ofs < < " \n # Compiled Object files " ;
ofs < < " \n *.slo " ;
ofs < < " \n *.lo " ;
ofs < < " \n *.o " ;
ofs < < " \n *.obj " ;
ofs < < " \n " ;
ofs < < " \n # Precompiled Headers " ;
ofs < < " \n *.gch " ;
ofs < < " \n *.pch " ;
ofs < < " \n " ;
ofs < < " \n # Compiled Dynamic libraries " ;
ofs < < " \n *.so " ;
ofs < < " \n *.dylib " ;
ofs < < " \n *.dll " ;
ofs < < " \n " ;
ofs < < " \n # Fortran module files " ;
ofs < < " \n *.mod " ;
ofs < < " \n *.smod " ;
ofs < < " \n " ;
ofs < < " \n # Compiled Static libraries " ;
ofs < < " \n *.lai " ;
ofs < < " \n *.la " ;
ofs < < " \n *.a " ;
ofs < < " \n *.lib " ;
ofs < < " \n " ;
ofs < < " \n # Executables " ;
ofs < < " \n *.exe " ;
ofs < < " \n *.out " ;
ofs < < " \n *.app " ;
ofs < < " \n " ;
ofs < < " \n # other " ;
ofs < < " \n *.log " ;
ofs < < " \n *.zip " ;
ofs < < " \n *.ini " ;
ofs < < " \n " ;
ofs < < " \n ######################## CMAKE IGNORES " ;
ofs < < " \n CMakeLists.txt.user " ;
ofs < < " \n CMakeCache.txt " ;
ofs < < " \n CMakeFiles " ;
ofs < < " \n CMakeScripts " ;
ofs < < " \n Testing " ;
ofs < < " \n Makefile " ;
ofs < < " \n cmake_install.cmake " ;
ofs < < " \n install_manifest.txt " ;
ofs < < " \n compile_commands.json " ;
ofs < < " \n CTestTestfile.cmake " ;
ofs < < " \n _deps " ;
ofs < < " \n " ;
ofs < < " \n ######################## BUILD IGNORES " ;
ofs < < " \n build/ " ;
ofs . close ( ) ;
ofs . clear ( ) ;
}
////////////////////////////////////////////////////////////
// Generate Script files
////////////////////////////////////////////////////////////
bool have_platform_option = argParser . ContainsOption ( " -p " ) ;
bool gen_windows = ( ! have_platform_option | | ( argParser . GetOptionValue ( " -p " ) . values [ 0 ] = = " win " | | argParser . GetOptionValue ( " -p " ) . values [ 0 ] = = " both " ) ) ;
bool gen_linux = ( have_platform_option & & ( argParser . GetOptionValue ( " -p " ) . values [ 0 ] = = " linux " | | argParser . GetOptionValue ( " -p " ) . values [ 0 ] = = " both " ) ) ;
std : : filesystem : : path script_dir = root / " scripts " ;
if ( gen_windows )
{
// CONFIG
std : : filesystem : : path config_file = script_dir / " config.bat " ;
ofs = std : : ofstream ( config_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the config script file: " < < config_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating config script file: " < < config_file . string ( ) . c_str ( ) ;
ofs < < " @echo off " ;
ofs < < " \n REM This script expects to be run from the parent directory " ;
ofs < < " \n REM ex. scripts/cmconfig.bat " ;
ofs < < " \n @echo off " ;
ofs < < " \n \n cmake -Wno-dev -B build/ -S . -G \" Visual Studio 16 2019 \" -A x64 " ;
ofs . close ( ) ;
ofs . clear ( ) ;
// BUILD
std : : filesystem : : path build_file = script_dir / " build.bat " ;
ofs = std : : ofstream ( build_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the build script file: " < < build_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating build script file: " < < build_file . string ( ) . c_str ( ) ;
ofs < < " @echo off " ;
ofs < < " \n REM This script expects to be run from the parent directory " ;
ofs < < " \n REM ex. scripts/build.bat " ;
ofs < < " \n \n IF not exist build/ ( " ;
ofs < < " \n \t echo This script needs to be run from the directory above build/ " ;
ofs < < " \n \t goto END " ;
ofs < < " \n ) " ;
ofs < < " \n \n IF \" %~1 \" == \" r \" ( " ;
ofs < < " \n \t cmake --build build/ --target ALL_BUILD --config Release " ;
ofs < < " \n ) ELSE ( " ;
ofs < < " \n \t cmake --build build/ --target ALL_BUILD --config Debug " ;
ofs < < " \n ) " ;
ofs < < " \n \n :END " ;
ofs . close ( ) ;
ofs . clear ( ) ;
// CLEAN
std : : filesystem : : path clean_file = script_dir / " clean.bat " ;
ofs = std : : ofstream ( clean_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the clean script file: " < < clean_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating clean script file: " < < clean_file . string ( ) . c_str ( ) ;
ofs < < " @echo off " ;
ofs < < " \n \n IF not exist build/ ( " ;
ofs < < " \n \t echo This script needs to be run from the directory above build/ " ;
ofs < < " \n \t goto END " ;
ofs < < " \n ) " ;
ofs < < " \n \n echo Removing the build directory " ;
ofs < < " \n del /s /q build " ;
ofs < < " \n rd /s /q build " ;
ofs < < " \n \n :END " ;
ofs . close ( ) ;
ofs . clear ( ) ;
}
if ( gen_linux )
{
// CONFIG
std : : filesystem : : path config_file = script_dir / " config.sh " ;
ofs = std : : ofstream ( config_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the config script file: " < < config_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating config script file: " < < config_file . string ( ) . c_str ( ) ;
ofs < < " #! /bin/sh " ;
ofs < < " \n # This script expects to be run from the parent directory " ;
ofs < < " \n # ex. scripts/cmconfig.sh " ;
ofs < < " \n \n cmake -Wno-dev -DBOX2D_BUILD_TESTBED=OFF -S . -B build/ " ;
ofs . close ( ) ;
ofs . clear ( ) ;
// BUILD
std : : filesystem : : path build_file = script_dir / " build.sh " ;
ofs = std : : ofstream ( build_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the build script file: " < < build_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating build script file: " < < build_file . string ( ) . c_str ( ) ;
ofs < < " #! /bin/sh " ;
ofs < < " \n # This script expects to be run from the parent directory " ;
ofs < < " \n # ex. scripts/build.sh " ;
ofs < < " \n \n make -C build/ " ;
ofs . close ( ) ;
ofs . clear ( ) ;
}
////////////////////////////////////////////////////////////
// Generate Main.cpp
////////////////////////////////////////////////////////////
std : : filesystem : : path main_file = root / " src/main.cpp " ;
ofs = std : : ofstream ( main_file . string ( ) . c_str ( ) ) ;
if ( ! ofs . is_open ( ) )
{
std : : cout < < " \n Could not generate the config script file: " < < main_file . string ( ) . c_str ( ) ;
return 1 ;
}
std : : cout < < " \n Generating config script file: " < < main_file . string ( ) . c_str ( ) ;
ofs < < " \n \n #include <iostream> " ;
ofs < < " \n #include < " < < project_name < < " Config.h> " ;
ofs < < " \n \n int main(int argc, char** argv) " ;
ofs < < " \n { " ;
ofs < < " \n \t std::cout << \" Hello, World! \" ; " ;
ofs < < " \n \t std::cout << \" \\ nThis is version: \" << " < < project_name < < " _VERSION_MAJOR << \" . \" << " < < project_name < < " _VERSION_MINOR << \" . \" << " < < project_name < < " _VERSION_PATCH; " ;
ofs < < " \n \t return 0; " ;
ofs < < " \n } " ;
ofs . close ( ) ;
ofs . clear ( ) ;
std : : cout < < " \n \n New project generated! " ;
return 0 ;
}