init: ready, set, go!
This commit is contained in:
commit
930dafa31f
|
@ -0,0 +1,13 @@
|
||||||
|
gcc
|
||||||
|
-std=c11
|
||||||
|
-Iinc
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wconversion
|
||||||
|
-Wdouble-promotion
|
||||||
|
-Wshadow
|
||||||
|
-Wcast-qual
|
||||||
|
-Wmissing-prototypes
|
||||||
|
-Werror
|
||||||
|
-pedantic
|
||||||
|
-DDEBUG
|
|
@ -0,0 +1,4 @@
|
||||||
|
/bin
|
||||||
|
/obj
|
||||||
|
/.ccls-cache
|
||||||
|
__pycache__
|
|
@ -0,0 +1,230 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import config
|
||||||
|
import dataclasses
|
||||||
|
import datetime
|
||||||
|
import enum
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class File:
|
||||||
|
'''
|
||||||
|
stores source file, the path to the generated object file and a list of
|
||||||
|
header dependencies.
|
||||||
|
'''
|
||||||
|
src: str
|
||||||
|
obj: str
|
||||||
|
deps: list[str]
|
||||||
|
|
||||||
|
|
||||||
|
class CompilationMode(enum.Enum):
|
||||||
|
'''
|
||||||
|
used to specify if the program should be compiled in debug or release mode.
|
||||||
|
'''
|
||||||
|
Debug = 'debug'
|
||||||
|
Release = 'release'
|
||||||
|
|
||||||
|
|
||||||
|
class Color(enum.Enum):
|
||||||
|
'''
|
||||||
|
common colors used for colorizing terminal output.
|
||||||
|
'''
|
||||||
|
Reset = '\x1b[0m'
|
||||||
|
Red = '\x1b[31m'
|
||||||
|
Green = '\x1b[32m'
|
||||||
|
|
||||||
|
|
||||||
|
def get_source_files() -> list[str]:
|
||||||
|
'''
|
||||||
|
get a list of source files located in the `src/` folder. only matches files
|
||||||
|
ending in `*.c`.
|
||||||
|
'''
|
||||||
|
path = pathlib.Path('src/')
|
||||||
|
files = [str(p) for p in path.rglob('*.c')]
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def get_object_location(src_file: str, compilation_mode: CompilationMode) -> str:
|
||||||
|
'''
|
||||||
|
get the location of the resulting object file. for example, a source file
|
||||||
|
`src/foo/bar/baz.c` will have the object location `obj/debug/foo/bar/baz.c`
|
||||||
|
if compiled in debug mode.
|
||||||
|
'''
|
||||||
|
result = src_file.replace('src/', f'obj/{compilation_mode.value}/')
|
||||||
|
result = result[:-2] + '.o'
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_dependencies(src_file: str) -> list[str]:
|
||||||
|
'''
|
||||||
|
obtain a list of header dependencies for a source file by running `cc -M`.
|
||||||
|
since `cc -M` uses make as its output format, it needs to be converted to a
|
||||||
|
regular python list.
|
||||||
|
'''
|
||||||
|
# obtain raw make-compatible output from `cc -M` and remove linebreaks
|
||||||
|
output_raw = subprocess.check_output([config.COMPILER, '-Iinc', '-M', '-MM', src_file])
|
||||||
|
output = output_raw.decode('utf-8').replace('\n', '').replace('\\', '')
|
||||||
|
# remove make rule to get space-delimited list of dependencies
|
||||||
|
deps = output.split(':')[1].split(' ')
|
||||||
|
# filter for header files
|
||||||
|
deps = list(filter(lambda x: x.endswith('.h'), deps))
|
||||||
|
return deps
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_list(compilation_mode: CompilationMode) -> list[File]:
|
||||||
|
'''
|
||||||
|
get a list of `File` objects that contain information about where source
|
||||||
|
files are, where their object files will be located and which headers they
|
||||||
|
depend on.
|
||||||
|
'''
|
||||||
|
src_files = get_source_files()
|
||||||
|
files: list[File] = []
|
||||||
|
|
||||||
|
for src in src_files:
|
||||||
|
obj = get_object_location(src, compilation_mode)
|
||||||
|
deps = get_dependencies(src)
|
||||||
|
files.append(File(src, obj, deps))
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def log_message(color: Color, prefix: str, msg: str):
|
||||||
|
'''
|
||||||
|
print a message to the terminal using a specific color and prefix. the
|
||||||
|
message will look like this:
|
||||||
|
PRFX message
|
||||||
|
where PRFX is the passed prefix. the prefix is colored according to the
|
||||||
|
parameter passed to this function.
|
||||||
|
'''
|
||||||
|
print(f'{color.value}{prefix.ljust(4).upper()}{Color.Reset.value} {msg}')
|
||||||
|
|
||||||
|
|
||||||
|
def check_if_need_compile(file: File) -> bool:
|
||||||
|
'''
|
||||||
|
checks if an object file needs to be re-compiled based on the modification
|
||||||
|
dates of its source file and header dependencies.
|
||||||
|
'''
|
||||||
|
# if the object file does not exist, it needs to be compiled
|
||||||
|
obj_path = pathlib.Path(file.obj)
|
||||||
|
if not obj_path.exists():
|
||||||
|
return True
|
||||||
|
|
||||||
|
# if the object file is older than the source file, it needs to be compiled
|
||||||
|
src_path = pathlib.Path(file.src)
|
||||||
|
src_mod_time = datetime.datetime.fromtimestamp(src_path.stat().st_mtime, tz=datetime.timezone.utc)
|
||||||
|
obj_mod_time = datetime.datetime.fromtimestamp(obj_path.stat().st_mtime, tz=datetime.timezone.utc)
|
||||||
|
if obj_mod_time < src_mod_time:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# check all headers if they were modified since the object file was created
|
||||||
|
for dep in file.deps:
|
||||||
|
header_path = pathlib.Path(dep)
|
||||||
|
header_mod_time = datetime.datetime.fromtimestamp(header_path.stat().st_mtime, tz=datetime.timezone.utc)
|
||||||
|
if obj_mod_time < header_mod_time:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# file does not need to be compiled
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def compile_file(file: File, compilation_mode: CompilationMode) -> int:
|
||||||
|
'''
|
||||||
|
compile a source file to an object file.
|
||||||
|
'''
|
||||||
|
if not check_if_need_compile(file):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
path = pathlib.Path(file.obj)
|
||||||
|
|
||||||
|
obj_folder = str(path.parent)
|
||||||
|
os.makedirs(obj_folder, exist_ok=True)
|
||||||
|
|
||||||
|
if compilation_mode == CompilationMode.Debug:
|
||||||
|
mode_specific_flags = config.FLAGS_DEBUG
|
||||||
|
elif compilation_mode == CompilationMode.Release:
|
||||||
|
mode_specific_flags = config.FLAGS_RELEASE
|
||||||
|
|
||||||
|
log_message(Color.Green, 'cc', file.obj)
|
||||||
|
|
||||||
|
return_code = subprocess.call([
|
||||||
|
config.COMPILER,
|
||||||
|
*config.FLAGS.split(' '),
|
||||||
|
*config.FLAGS_WARN.split(' '),
|
||||||
|
*mode_specific_flags.split(' '),
|
||||||
|
'-Iinc',
|
||||||
|
'-c', file.src,
|
||||||
|
'-o', file.obj,
|
||||||
|
])
|
||||||
|
|
||||||
|
return return_code
|
||||||
|
|
||||||
|
|
||||||
|
def link_program(files: list[File], compilation_mode: CompilationMode) -> int:
|
||||||
|
'''
|
||||||
|
link the generated object files to an executable binary.
|
||||||
|
'''
|
||||||
|
os.makedirs('bin/', exist_ok=True)
|
||||||
|
|
||||||
|
objs = [file.obj for file in files]
|
||||||
|
|
||||||
|
if compilation_mode == CompilationMode.Debug:
|
||||||
|
mode_specific_flags = config.FLAGS_DEBUG
|
||||||
|
elif compilation_mode == CompilationMode.Release:
|
||||||
|
mode_specific_flags = config.FLAGS_RELEASE
|
||||||
|
|
||||||
|
target = f'bin/{config.TARGET}-{compilation_mode.value}'
|
||||||
|
|
||||||
|
log_message(Color.Green, 'link', target)
|
||||||
|
|
||||||
|
return_code = subprocess.call([
|
||||||
|
config.COMPILER,
|
||||||
|
*config.FLAGS.split(' '),
|
||||||
|
*config.FLAGS_WARN.split(' '),
|
||||||
|
*mode_specific_flags.split(' '),
|
||||||
|
*config.LIBS,
|
||||||
|
'-Iinc',
|
||||||
|
*objs,
|
||||||
|
'-o', target,
|
||||||
|
])
|
||||||
|
|
||||||
|
return return_code
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# parse command line arguments
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
compilation_mode = CompilationMode.Debug
|
||||||
|
elif sys.argv[1] == 'debug':
|
||||||
|
compilation_mode = CompilationMode.Debug
|
||||||
|
elif sys.argv[1] == 'release':
|
||||||
|
compilation_mode = CompilationMode.Release
|
||||||
|
elif sys.argv[1] == 'clean':
|
||||||
|
subprocess.call(['rm', '-rf', 'bin/', 'obj/'])
|
||||||
|
exit(0)
|
||||||
|
else:
|
||||||
|
log_message(Color.Red, 'err', f'unknown subcommand `{sys.argv[1]}`')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# obtain source files
|
||||||
|
files = get_file_list(compilation_mode)
|
||||||
|
|
||||||
|
# compile object files
|
||||||
|
for file in files:
|
||||||
|
success = compile_file(file, compilation_mode)
|
||||||
|
if success != 0:
|
||||||
|
log_message(Color.Red, 'err', f'failed to compile {file.obj}')
|
||||||
|
exit(success)
|
||||||
|
|
||||||
|
# generate executable
|
||||||
|
success = link_program(files, compilation_mode)
|
||||||
|
if success != 0:
|
||||||
|
log_message(Color.Red, 'err', f'failed to link program')
|
||||||
|
exit(success)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,7 @@
|
||||||
|
COMPILER = 'gcc'
|
||||||
|
FLAGS = '-std=c11'
|
||||||
|
FLAGS_WARN = '-Wall -Wextra -Wconversion -Wdouble-promotion -Wshadow -Wcast-qual -Wmissing-prototypes -Wmissing-noreturn -Wredundant-decls -Wdisabled-optimization -Wunsafe-loop-optimizations -Wcast-align=strict -Winline -Wvla -Wlogical-op -Wdate-time -Werror -pedantic'
|
||||||
|
FLAGS_DEBUG = '-O0 -ggdb3 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -fstack-protector -DDEBUG'
|
||||||
|
FLAGS_RELEASE = '-O3 -march=native -DNDEBUG'
|
||||||
|
LIBS = ''
|
||||||
|
TARGET = 'c-template'
|
|
@ -0,0 +1,100 @@
|
||||||
|
#ifndef COMMON_H_
|
||||||
|
#define COMMON_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
|
||||||
|
/** macros ********************************************************************/
|
||||||
|
|
||||||
|
/// define debug macros
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
# define DEBUG_ALLOC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// c11 / c23 compatibility. many of these can be removed once the project fully
|
||||||
|
/// migrates to c23, but for now, some of these will have to be used. when
|
||||||
|
/// migrating, remember that the usage of macros like `_Noreturn` should be
|
||||||
|
/// replaced by c23-specific syntax, in this case the `[[noreturn]]` attribute.
|
||||||
|
|
||||||
|
// if the program is compiled with c23 or newer, define `STDC23` as a feature
|
||||||
|
// test macro that is used for enabling features based on if c23 or c11 is used.
|
||||||
|
|
||||||
|
#if __STDC_VERSION__ >= 202000L
|
||||||
|
# define STDC23
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// enable C11-specific features
|
||||||
|
|
||||||
|
#if !defined(STDC23)
|
||||||
|
# define nullptr NULL /* `nullptr` from C23 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// enable C23-specific features
|
||||||
|
|
||||||
|
#if defined(STDC23)
|
||||||
|
# define _Noreturn [[noreturn]]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// function wrappers
|
||||||
|
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
# define smalloc(nmemb, size) __smalloc (nmemb, size, __FILE__, __LINE__)
|
||||||
|
# define scalloc(nmemb, size) __scalloc (nmemb, size, __FILE__, __LINE__)
|
||||||
|
# define srealloc(ptr, nmemb, size) __srealloc (ptr, nmemb, size, __FILE__, __LINE__)
|
||||||
|
#else
|
||||||
|
# define smalloc(nmemb, size) __smalloc (nmemb, size)
|
||||||
|
# define scalloc(nmemb, size) __scalloc (nmemb, size)
|
||||||
|
# define srealloc(ptr, nmemb, size) __srealloc (ptr, nmemb, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** typedefs ******************************************************************/
|
||||||
|
|
||||||
|
/// common type abbreviations
|
||||||
|
|
||||||
|
typedef signed char schar;
|
||||||
|
typedef signed long long llong;
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
typedef unsigned short ushort;
|
||||||
|
typedef unsigned int uint;
|
||||||
|
typedef unsigned long ulong;
|
||||||
|
typedef unsigned long long ullong;
|
||||||
|
typedef int8_t i8;
|
||||||
|
typedef int16_t i16;
|
||||||
|
typedef int32_t i32;
|
||||||
|
typedef int64_t i64;
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
typedef ssize_t isize;
|
||||||
|
typedef size_t usize;
|
||||||
|
typedef float f32;
|
||||||
|
typedef double f64;
|
||||||
|
typedef long double f128;
|
||||||
|
|
||||||
|
|
||||||
|
/** functions *****************************************************************/
|
||||||
|
|
||||||
|
/// safe memory allocation
|
||||||
|
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
void *__smalloc (const usize nmemb, const usize size, const char *const file, const usize line);
|
||||||
|
void *__scalloc (const usize nmemb, const usize size, const char *const file, const usize line);
|
||||||
|
void *__srealloc (void *ptr, const usize nmemb, const usize size, const char *const file, const usize line);
|
||||||
|
void sfree (void *ptr);
|
||||||
|
#else
|
||||||
|
void *__smalloc (const usize nmemb, const usize size);
|
||||||
|
void *__scalloc (const usize nmemb, const usize size);
|
||||||
|
void *__srealloc (void *ptr, const usize nmemb, const usize size);
|
||||||
|
void sfree (void *ptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // COMMON_H_
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef LOG_H_
|
||||||
|
#define LOG_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/** macros ********************************************************************/
|
||||||
|
|
||||||
|
#define log_debug(...) __log_print (__Log_Level_Debug, __VA_ARGS__)
|
||||||
|
#define log_info(...) __log_print (__Log_Level_Info, __VA_ARGS__)
|
||||||
|
#define log_ok(...) __log_print (__Log_Level_Ok, __VA_ARGS__)
|
||||||
|
#define log_warn(...) __log_print (__Log_Level_Warn, __VA_ARGS__)
|
||||||
|
#define log_err(...) __log_print (__Log_Level_Err, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define log_die(...) \
|
||||||
|
do { \
|
||||||
|
__log_print(__Log_Level_Err, __VA_ARGS__); \
|
||||||
|
exit(EXIT_FAILURE); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/** enums *********************************************************************/
|
||||||
|
|
||||||
|
enum __Log_Level {
|
||||||
|
__Log_Level_Debug,
|
||||||
|
__Log_Level_Info,
|
||||||
|
__Log_Level_Ok,
|
||||||
|
__Log_Level_Warn,
|
||||||
|
__Log_Level_Err,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** functions *****************************************************************/
|
||||||
|
|
||||||
|
void __log_print (enum __Log_Level level, const char *const fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LOG_H_
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** functions *****************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe `malloc()` wrapper. instead of returning `nullptr` on error, this
|
||||||
|
* function will crash the program. returns a pointer to the allocated memory.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
__smalloc (const usize nmemb, /* number of elements to allocate */
|
||||||
|
const usize size /* size of each element */
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
,
|
||||||
|
const char *const file, /* __FILE__ */
|
||||||
|
const usize line /* __LINE__ */
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
ptr = malloc (nmemb * size);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
log_die ("\x1b[90m(\x1b[35m%s\x1b[90m:\x1b[34m%zu\x1b[90m)\x1b[0m "
|
||||||
|
"failed to allocate %zu bytes of memory.\n",
|
||||||
|
file, line, nmemb * size);
|
||||||
|
#else
|
||||||
|
log_die ("failed to allocate %zu bytes of memory.\n", nmemb * size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe `calloc()` wrapper. instead of returning `nullptr` on error, this
|
||||||
|
* function will crash the program. returns a pointer to the allocated memory.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
__scalloc (const usize nmemb, /* number of elements to allocate */
|
||||||
|
const usize size /* size of each element */
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
,
|
||||||
|
const char *const file, /* __FILE__ */
|
||||||
|
const usize line /* __LINE__ */
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
ptr = calloc (nmemb, size);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
log_die ("\x1b[90m(\x1b[35m%s\x1b[90m:\x1b[34m%zu\x1b[90m)\x1b[0m "
|
||||||
|
"failed to allocate %zu bytes of memory.\n",
|
||||||
|
file, line, nmemb * size);
|
||||||
|
#else
|
||||||
|
log_die ("failed to allocate %zu bytes of memory.\n", nmemb * size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe `realloc()` wrapper. instead of returning `nullptr` on error, this
|
||||||
|
* function will crash the program. returns a pointer to the reallocated memory.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
__srealloc (void *ptr, /* the pointer to reallocate */
|
||||||
|
const usize nmemb, /* number of elements to allocate */
|
||||||
|
const usize size /* size of each element */
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
,
|
||||||
|
const char *const file, /* __FILE__ */
|
||||||
|
const usize line /* __LINE__ */
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
ptr = realloc (ptr, nmemb * size);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
log_die ("\x1b[90m(\x1b[35m%s\x1b[90m:\x1b[34m%zu\x1b[90m)\x1b[0m "
|
||||||
|
"failed to allocate %zu bytes of memory.\n",
|
||||||
|
file, line, nmemb * size);
|
||||||
|
#else
|
||||||
|
log_die ("failed to allocate %zu bytes of memory.\n", nmemb * size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wrapper for `free()`. will not call `free()` on pointers that are `nullptr`.
|
||||||
|
* this is technically not necessary according to the c standard, but you never
|
||||||
|
* know.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sfree (void *ptr) /* the pointer to free */
|
||||||
|
{
|
||||||
|
if (ptr != nullptr) {
|
||||||
|
free (ptr);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include "log.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/** functions *****************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print a formatted message to stderr using printf formatting and a given log
|
||||||
|
* level. this functions is not intended to be used directly. the `log_*` macros
|
||||||
|
* should be used instead.
|
||||||
|
*/
|
||||||
|
__attribute__ ((format (printf, 2, 3)))
|
||||||
|
void
|
||||||
|
__log_print (enum __Log_Level level, /* log level */
|
||||||
|
const char *const fmt, /* format string */
|
||||||
|
...) /* format parameters */
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case __Log_Level_Debug: { fprintf (stderr, "\x1b[90m[\x1b[35mdebug\x1b[90m]\x1b[0m "); break; }
|
||||||
|
case __Log_Level_Info: { fprintf (stderr, "\x1b[90m[\x1b[34minfo\x1b[90m]\x1b[0m " ); break; }
|
||||||
|
case __Log_Level_Ok: { fprintf (stderr, "\x1b[90m[\x1b[32mok\x1b[90m]\x1b[0m " ); break; }
|
||||||
|
case __Log_Level_Warn: { fprintf (stderr, "\x1b[90m[\x1b[33mwarn\x1b[90m]\x1b[0m " ); break; }
|
||||||
|
case __Log_Level_Err: { fprintf (stderr, "\x1b[90m[\x1b[31merr\x1b[90m]\x1b[0m " ); break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start (ap, fmt);
|
||||||
|
vfprintf (stderr, fmt, ap);
|
||||||
|
va_end (ap);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main (const int argc, const char *const argv[])
|
||||||
|
{
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
|
||||||
|
log_debug ("Hello, World!\n");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue