├── C_binary_example ├── CMakeLists.txt ├── generate.c └── src │ ├── CMakeLists.txt │ └── main.c └── Python_example ├── .gitignore ├── CMakeLists.txt ├── Pipfile ├── README.md ├── config.json ├── generate.py └── src ├── CMakeLists.txt └── main.c /C_binary_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.5) 2 | project(example) 3 | 4 | include_directories(${PROJECT_BINARY_DIR}/src) 5 | 6 | # Set variable for the generate sources 7 | set(GENERATE_SRCS ${PROJECT_SOURCE_DIR}/generate.c) 8 | set(GENERATE_OUTPUT ${PROJECT_BINARY_DIR}/generate) 9 | set(VERSION_OUTPUT ${PROJECT_BINARY_DIR}/src/version.h) 10 | set(VERSION_MAJOR 0) 11 | set(VERSION_MINOR 0) 12 | set(VERSION_PATCH 0) 13 | set(VERSION_META "-alpha") 14 | 15 | # Create command to compile the generate command 16 | add_custom_command( 17 | OUTPUT ${GENERATE_OUTPUT} 18 | COMMAND gcc -o ${GENERATE_OUTPUT} ${GENERATE_SRCS} 19 | DEPENDS ${GENERATE_SRCS} 20 | ) 21 | 22 | add_custom_target(generate_version 23 | COMMAND ${GENERATE_OUTPUT} 24 | -M ${VERSION_MAJOR} 25 | -m ${VERSION_MINOR} 26 | -p ${VERSION_PATCH} 27 | -a "${VERSION_META}" 28 | -F "${PROJECT_BINARY_DIR}/buildnumber" 29 | -i 30 | > ${VERSION_OUTPUT} 31 | DEPENDS ${GENERATE_OUTPUT} 32 | VERBATIM 33 | ) 34 | 35 | add_subdirectory(src) 36 | -------------------------------------------------------------------------------- /C_binary_example/generate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define VERSION_H \ 9 | "#ifndef %s_VERSION_H\n" \ 10 | "#define %s_VERSION_H\n" \ 11 | "\n" \ 12 | "#define %s_VERSION_MAJOR %d\n" \ 13 | "#define %s_VERSION_MINOR %d\n" \ 14 | "#define %s_VERSION_PATCH %d\n" \ 15 | "#define %s_VERSION_BUILD %d\n" \ 16 | "#define %s_VERSION_META \"%s\"\n" \ 17 | "#define %s_VERSION \"%d.%d.%d.%d%s\"\n" \ 18 | "#define %s_COMPILE_DATE \"%s\"\n" \ 19 | "#define %s_COMPILE_TIME \"%s\"\n" \ 20 | "\n" \ 21 | "#endif /* %s_VERSION_H */\n" 22 | 23 | #define USAGE \ 24 | "Usage: %s [-hMmpbaPDTiB]\n" \ 25 | "\n" \ 26 | "Options:\n" \ 27 | " -h, --help Print this menu and exit.\n" \ 28 | " -M, --major Major number.\n" \ 29 | " -m, --minor Minor number.\n" \ 30 | " -p, --patch Patch number.\n" \ 31 | " -b, --build Build number.\n" \ 32 | " -a, --meta Meta information.\n" \ 33 | " -P, --prefix The macro prefix [default: \"%s\"]\n" \ 34 | " -D, --date Date format [default: \"%s\"]\n" \ 35 | " -T, --time Time format [default: \"%s\"]\n" \ 36 | " -i, --auto-build Auto increment build number.\n" \ 37 | " -F, --build-file The file containing the build number.\n" \ 38 | "\n" 39 | 40 | int main(int argc, char **argv) 41 | { 42 | struct option long_options[] = { 43 | {"help", required_argument, NULL, 'h'}, 44 | {"major", required_argument, NULL, 'M'}, 45 | {"minor", required_argument, NULL, 'm'}, 46 | {"patch", required_argument, NULL, 'p'}, 47 | {"build", required_argument, NULL, 'b'}, 48 | {"meta", required_argument, NULL, 'a'}, 49 | {"prefix", required_argument, NULL, 'P'}, 50 | {"date", required_argument, NULL, 'D'}, 51 | {"time", required_argument, NULL, 'T'}, 52 | {"auto-build", no_argument, NULL, 'i'}, 53 | {"build-file", required_argument, NULL, 'F'}, 54 | }; 55 | int major, minor, patch, build, c, auto_inc_build; 56 | char *meta, *prefix, *datefmt, *timefmt, *buildfile; 57 | time_t timer; 58 | struct tm *tm_info; 59 | char datestr[32]; 60 | char timestr[32]; 61 | 62 | memset(datestr, 0, sizeof(datestr)); 63 | memset(timestr, 0, sizeof(timestr)); 64 | 65 | major = minor = patch = build = auto_inc_build = 0; 66 | meta = ""; 67 | prefix = "EXAMPLE"; 68 | datefmt = "%Y-%m-%d"; 69 | timefmt = "%H:%M:%S"; 70 | buildfile = NULL; 71 | 72 | while ((c = getopt_long(argc, argv, "hM:m:p:b:a:P:D:T:iF:", 73 | long_options, 0)) != -1) { 74 | switch (c) { 75 | case 'h': 76 | printf(USAGE, argv[0], prefix, datefmt, timefmt); 77 | return 0; 78 | case 'M': 79 | major = atoi(optarg); 80 | break; 81 | case 'm': 82 | minor = atoi(optarg); 83 | break; 84 | case 'p': 85 | patch = atoi(optarg); 86 | break; 87 | case 'b': 88 | build = atoi(optarg); 89 | break; 90 | case 'a': 91 | meta = optarg; 92 | break; 93 | case 'P': 94 | prefix = optarg; 95 | break; 96 | case 'D': 97 | datefmt = optarg; 98 | break; 99 | case 'T': 100 | timefmt = optarg; 101 | break; 102 | case 'i': 103 | auto_inc_build = 1; 104 | break; 105 | case 'F': 106 | buildfile = optarg; 107 | break; 108 | case '?': 109 | exit(1); 110 | } 111 | } 112 | 113 | if (auto_inc_build) { 114 | if (buildfile) { 115 | /* 65535 builds should be plenty (plus null-terminator) */ 116 | char line[17]; 117 | FILE *fp = fopen(buildfile, "r"); 118 | if (fp == NULL) { 119 | /* Don't overwrite build number if we can't 120 | * open file; keep the existing value. */ 121 | build = build != 0 ? build : 0; 122 | } else { 123 | fread(line, sizeof(line), 1, fp); 124 | build = atoi(line); 125 | fclose(fp); 126 | } 127 | 128 | build++; 129 | /* Reopen to write new value; Truncate file. */ 130 | fp = fopen(buildfile, "w+"); 131 | if (fp == NULL) { 132 | fprintf(stderr, "%s: %s\n", buildfile, strerror(errno)); 133 | exit(1); 134 | } 135 | /* Overwrite existing build value. */ 136 | snprintf(line, sizeof(line), "%d", build); 137 | fprintf(fp, "%d", build); 138 | fclose(fp); 139 | } 140 | } 141 | 142 | time(&timer); 143 | tm_info = localtime(&timer); 144 | 145 | strftime(datestr, sizeof(datestr), datefmt, tm_info); 146 | strftime(timestr, sizeof(timestr), timefmt, tm_info); 147 | 148 | printf(VERSION_H, 149 | prefix, 150 | prefix, 151 | prefix, major, 152 | prefix, minor, 153 | prefix, patch, 154 | prefix, build, 155 | prefix, meta, 156 | prefix, major, minor, patch, build, meta, 157 | prefix, datestr, 158 | prefix, timestr, 159 | prefix); 160 | 161 | return 0; 162 | } 163 | -------------------------------------------------------------------------------- /C_binary_example/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRCS 2 | main.c 3 | ) 4 | add_executable(example ${SRCS}) 5 | add_dependencies(example generate_version) 6 | -------------------------------------------------------------------------------- /C_binary_example/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "version.h" 4 | 5 | int main(int argc, char **argv) 6 | { 7 | printf("version: %s\n", EXAMPLE_VERSION); 8 | printf("compiled: %s %s\n", EXAMPLE_COMPILE_DATE, EXAMPLE_COMPILE_TIME); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /Python_example/.gitignore: -------------------------------------------------------------------------------- 1 | Pipfile.lock 2 | src/version.h 3 | src/version.c 4 | 5 | # Prerequisites 6 | *.d 7 | 8 | # Object files 9 | *.o 10 | *.ko 11 | *.obj 12 | *.elf 13 | 14 | # Linker output 15 | *.ilk 16 | *.map 17 | *.exp 18 | 19 | # Precompiled Headers 20 | *.gch 21 | *.pch 22 | 23 | # Libraries 24 | *.lib 25 | *.a 26 | *.la 27 | *.lo 28 | 29 | # Shared objects (inc. Windows DLLs) 30 | *.dll 31 | *.so 32 | *.so.* 33 | *.dylib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | *.i*86 40 | *.x86_64 41 | *.hex 42 | 43 | # Debug files 44 | *.dSYM/ 45 | *.su 46 | *.idb 47 | *.pdb 48 | 49 | # Kernel Module Compile Results 50 | *.mod* 51 | *.cmd 52 | .tmp_versions/ 53 | modules.order 54 | Module.symvers 55 | Mkfile.old 56 | dkms.conf 57 | 58 | # Byte-compiled / optimized / DLL files 59 | __pycache__/ 60 | *.py[cod] 61 | *$py.class 62 | 63 | # C extensions 64 | *.so 65 | 66 | # Distribution / packaging 67 | .Python 68 | build/ 69 | develop-eggs/ 70 | dist/ 71 | downloads/ 72 | eggs/ 73 | .eggs/ 74 | lib/ 75 | lib64/ 76 | parts/ 77 | sdist/ 78 | var/ 79 | wheels/ 80 | share/python-wheels/ 81 | *.egg-info/ 82 | .installed.cfg 83 | *.egg 84 | MANIFEST 85 | 86 | # PyInstaller 87 | # Usually these files are written by a python script from a template 88 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 89 | *.manifest 90 | *.spec 91 | 92 | # Installer logs 93 | pip-log.txt 94 | pip-delete-this-directory.txt 95 | 96 | # Unit test / coverage reports 97 | htmlcov/ 98 | .tox/ 99 | .nox/ 100 | .coverage 101 | .coverage.* 102 | .cache 103 | nosetests.xml 104 | coverage.xml 105 | *.cover 106 | *.py,cover 107 | .hypothesis/ 108 | .pytest_cache/ 109 | cover/ 110 | 111 | # Translations 112 | *.mo 113 | *.pot 114 | 115 | # Django stuff: 116 | *.log 117 | local_settings.py 118 | db.sqlite3 119 | db.sqlite3-journal 120 | 121 | # Flask stuff: 122 | instance/ 123 | .webassets-cache 124 | 125 | # Scrapy stuff: 126 | .scrapy 127 | 128 | # Sphinx documentation 129 | docs/_build/ 130 | 131 | # PyBuilder 132 | .pybuilder/ 133 | target/ 134 | 135 | # Jupyter Notebook 136 | .ipynb_checkpoints 137 | 138 | # IPython 139 | profile_default/ 140 | ipython_config.py 141 | 142 | # pyenv 143 | # For a library or package, you might want to ignore these files since the code is 144 | # intended to run in multiple environments; otherwise, check them in: 145 | # .python-version 146 | 147 | # pipenv 148 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 149 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 150 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 151 | # install all needed dependencies. 152 | #Pipfile.lock 153 | 154 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 155 | __pypackages__/ 156 | 157 | # Celery stuff 158 | celerybeat-schedule 159 | celerybeat.pid 160 | 161 | # SageMath parsed files 162 | *.sage.py 163 | 164 | # Environments 165 | .env 166 | .venv 167 | env/ 168 | venv/ 169 | ENV/ 170 | env.bak/ 171 | venv.bak/ 172 | 173 | # Spyder project settings 174 | .spyderproject 175 | .spyproject 176 | 177 | # Rope project settings 178 | .ropeproject 179 | 180 | # mkdocs documentation 181 | /site 182 | 183 | # mypy 184 | .mypy_cache/ 185 | .dmypy.json 186 | dmypy.json 187 | 188 | # Pyre type checker 189 | .pyre/ 190 | 191 | # pytype static type analyzer 192 | .pytype/ 193 | 194 | # Cython debug symbols 195 | cython_debug/ 196 | -------------------------------------------------------------------------------- /Python_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.5) 2 | project(example) 3 | 4 | find_package(Python3 COMPONENTS Interpreter REQUIRED) 5 | find_program(PipEnv pipenv REQUIRED) 6 | 7 | add_custom_command( 8 | OUTPUT 9 | ${PROJECT_SOURCE_DIR}/src/version.h 10 | ${PROJECT_SOURCE_DIR}/src/version.c 11 | COMMAND ${PipEnv} run ${Python3_EXECUTABLE} generate.py -o src/ config.json 12 | COMMENT "Generating version code [${PipEnv} run generate.py -o src/ config.json]" 13 | DEPENDS 14 | Pipfile.lock 15 | generate.py 16 | config.json 17 | WORKING_DIRECTORY 18 | ${CMAKE_CURRENT_SOURCE_DIR} 19 | ) 20 | 21 | add_executable( 22 | example 23 | src/main.c 24 | src/version.c 25 | ) 26 | target_link_libraries(example version) 27 | -------------------------------------------------------------------------------- /Python_example/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | docopt = "*" 10 | 11 | [requires] 12 | python_version = "3.8" 13 | -------------------------------------------------------------------------------- /Python_example/README.md: -------------------------------------------------------------------------------- 1 | A simple example to use a Python script to generate a C/C++ source file. 2 | 3 | # Synopsis 4 | 5 | Use `config.json` to store data that `generate.py` will use to generate C/C++ source files (including `.h`, `.c`, `.cpp`, etc) which will then be used in `src/main.c`. -------------------------------------------------------------------------------- /Python_example/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "EXAMPLE_", 3 | "major": 1, 4 | "minor": 0, 5 | "patch": 0, 6 | "build": 0, 7 | "meta": "" 8 | } -------------------------------------------------------------------------------- /Python_example/generate.py: -------------------------------------------------------------------------------- 1 | ''' 2 | USAGE: 3 | generate.py [-o ] 4 | 5 | OPTIONS: 6 | -v, --version Print the version 7 | -h, --help Print this help menu 8 | -o, --output Write output files to this directory 9 | ''' 10 | import os 11 | import sys 12 | import json 13 | from textwrap import dedent 14 | from datetime import datetime 15 | from docopt import docopt 16 | 17 | class CppGenerator: 18 | def __init__(self, version): 19 | self.decorated = version 20 | 21 | def header(self): 22 | now = datetime.now() 23 | return dedent('''\ 24 | #ifndef {prefix}VERSION_H 25 | #define {prefix}VERSION_H 26 | 27 | #define {prefix}VERSION_MAJOR {major} 28 | #define {prefix}VERSION_MINOR {minor} 29 | #define {prefix}VERSION_PATCH {patch} 30 | #define {prefix}VERSION_BUILD {build} 31 | #define {prefix}VERSION_META "{meta}" 32 | #define {prefix}VERSION "{major}.{minor}.{patch}.{build}{meta}" 33 | #define {prefix}COMPILE_DATE "{date}" 34 | #define {prefix}COMPILE_TIME "{time}" 35 | 36 | const char * hello_world(); 37 | 38 | #endif /* {prefix}VERSION_H */ 39 | '''.format( 40 | prefix = self.decorated['prefix'] if 'prefix' in self.decorated else '', 41 | major = self.decorated['major'] if 'major' in self.decorated else 0, 42 | minor = self.decorated['minor'] if 'minor' in self.decorated else 0, 43 | patch = self.decorated['patch'] if 'patch' in self.decorated else 0, 44 | build = self.decorated['build'] if 'build' in self.decorated else 0, 45 | meta = self.decorated['meta'] if 'meta' in self.decorated else '', 46 | date = now.strftime('%d/%m/%Y'), 47 | time = now.strftime('%H:%M:%S') 48 | )) 49 | 50 | def source(self): 51 | return dedent('''\ 52 | #include "version.h" 53 | 54 | const char * hello_world() 55 | { 56 | return "Hello World"; 57 | } 58 | ''') 59 | 60 | def main(): 61 | argv = docopt(__doc__, version="1.0") 62 | 63 | if argv[''] is not None: 64 | with open(argv['']) as config_file: 65 | config = json.load(config_file) 66 | gen = CppGenerator(config) 67 | if argv['--output']: 68 | header_file = os.path.join(argv['--output'], 'version.h') 69 | source_file = os.path.join(argv['--output'], 'version.c') 70 | with open(header_file, 'w') as out: 71 | out.write(gen.header()) 72 | with open(source_file, 'w') as out: 73 | out.write(gen.source()) 74 | else: 75 | print(gen.header()) 76 | print(gen.source()) 77 | 78 | if __name__ == "__main__": 79 | main() 80 | -------------------------------------------------------------------------------- /Python_example/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRCS 2 | main.c 3 | version.c 4 | ) 5 | add_executable(example ${SRCS}) 6 | add_dependencies(example generate_version) 7 | -------------------------------------------------------------------------------- /Python_example/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "version.h" 4 | 5 | int main(int argc, char **argv) 6 | { 7 | printf("version: %s\n", EXAMPLE_VERSION); 8 | 9 | printf("compiled: %s %s\n", EXAMPLE_COMPILE_DATE, EXAMPLE_COMPILE_TIME); 10 | printf("%s\n", hello_world()); 11 | return 0; 12 | } 13 | --------------------------------------------------------------------------------