This commit is contained in:
2025-08-01 21:32:27 -04:00
parent 3cdd1961a2
commit c48809aea9
8 changed files with 209 additions and 140 deletions

6
TODO Normal file
View File

@@ -0,0 +1,6 @@
1 Fix arg parseing
2 Fix generated Makefile
3 Add Licence
Ideas
1 Add --is-lib

BIN
c-out/bin/yait Executable file

Binary file not shown.

BIN
c-out/obj/main.o Normal file

Binary file not shown.

4
config.mak Normal file
View File

@@ -0,0 +1,4 @@
PREFIX=/usr/bin/
CFLAGS=-Wall -Wextra -ggdb
LDFLAGS=
CC=clang

View File

@@ -59,9 +59,7 @@ char *configure_template =
line ("done")
line ()
line ("printf \"checking for C compiler... \"")
line ("trycc gcc")
line ("trycc cc")
line ("trycc clang")
line ("%s")
line ("printf \"%s\\n\" \"$CC\"")
line ()
line ("printf \"checking weather C compiler works... \"")
@@ -87,7 +85,7 @@ char *makefile_template =
line ("prefix = /usr/bin")
line ()
line ("%s_SRCS := $(shell find . -name '%s/*.c')")
line ("%s_OBJS := $(patsubst ./%.c,c-out/obj/%%.o,$(%s_SRCS))")
line ("%s_OBJS := $(patsubst ./%%.c,c-out/obj/%%.o,$(%s_SRCS))")
line ()
line ("%s := c-out/bin/%s")
line ()
@@ -127,7 +125,7 @@ char *makefile_template =
line ("dist-clean: clean")
line ("\trm -f config.mak")
line ()
line (".PHONY: all clean dist-clean install uninstall build format");
line (".PHONY: all clean dist-clean install uninstall");
/* .clang-format template */
char *clang_format_template =

View File

@@ -11,9 +11,9 @@
}
#else
#define on_error(msg, code) \
if (err) \
if (code) \
{ \
printfn (msg ": %s", strerror (err)); \
printfn (msg ": %s", strerror (code)); \
return code; \
}
#endif

View File

@@ -3,39 +3,37 @@
* This file is part of yait
*
* This project and file is licenced under the BSD-3-Clause licence.
* <https://opensource.org/license/bsd-3-clause>
* <https://opensource.org/licence/bsd-3-clause>
*/
#ifndef FORMAT_H
#define FORMAT_H
#include <stdbool.h>
#include <string.h>
/* License type enumeration */
typedef enum
{
BSD3, /* BSD 3-Clause License */
GPLv3, /* GNU General Public License v3 */
MIT, /* MIT License */
UNlICENSE, /* Unlicense */
} license_t;
BSD3, /* BSD 3-Clause Licence */
GPLv3, /* GNU General Public Licence v3 */
MIT, /* MIT Licence */
UNLICENCE, /* Unlicence */
LICENCE_HELP, /* Help case */
} licence_t;
/* Library type enumeration - using bit flags for multiple selection */
/* A bit field is used so that we can accomplish two things. (a) store lots of
libraries without taxing memory; and (b) a dynamic array is not neccescary.
*/
typedef enum
{
LIB_NONE = 0, /* No libraries selected */
LIB_RAYLIB = 1 << 0, /* Raylib game library */
LIB_WINAPI = 1 << 1, /* Windows API */
LIB_NCURSES = 1 << 1, /* Windows API */
LIB_CURL = 1 << 2, /* cURL library */
/* Future libraries can be added here:
* LIB_OPENGL = 1 << 3,
* LIB_SDL2 = 1 << 4,
* LIB_GTK = 1 << 5,
* etc.
*/
LIB_COUNT_, /* Number of Libraries */
LIB_HELP, /* Help case */
} lib_flags_t;
/* Flag option type struct */
typedef struct
{
bool GNU;
@@ -44,26 +42,39 @@ typedef struct
bool use_cpp;
} flags_t;
/* Project configuration structure */
typedef struct
{
license_t license; /* License type for the project */
licence_t licence; /* Licence type for the project */
char *project; /* Project name */
char *name; /* Author/creator name */
char *name; /* Author/creator name ( if not provided infered on sanitize ) */
lib_flags_t libraries; /* Selected libraries (bit field) */
flags_t flag; /* Flags */
} format_t;
} manifest_t;
/* Default values */
#define DEFAULT_LICENSE BSD3
#define DEFAULT_GIT_INIT true
#define DEFAULT_CLANG_FORMAT true
#define DEFAULT_GIT_INIT true
#define DEFAULT_GNU false
#define DEFAULT_LIBRARIES LIB_NONE
#define DEFAULT_LICENCE BSD3
/* Helper macros for library operations */
#define HAS_LIBRARY(libs, lib) ((libs) & (lib))
#define ADD_LIBRARY(libs, lib) ((libs) |= (lib))
#define REMOVE_LIBRARY(libs, lib) ((libs) &= ~(lib))
#define CLEAR_LIBRARIES(libs) ((libs) = LIB_NONE)
static lib_flags_t
TOlibrary (char *src)
{
if (strcmp (src, "raylib"))
return LIB_RAYLIB;
if (strcmp (src, "ncurse"))
return LIB_NCURSES;
if (strcmp (src, "ncurses"))
return LIB_NCURSES;
if (strcmp (src, "curl"))
return LIB_CURL;
if (strcmp (src, "help"))
return LIB_HELP;
return LIB_COUNT_; /* bad case */
}
#endif

View File

@@ -3,7 +3,7 @@
* This file is part of yait
*
* This project and file is licenced under the BSD-3-Clause licence.
* <https://opensource.org/license/bsd-3-clause>
* <https://opensource.org/licence/bsd-3-clause>
*/
// Usage: yait [OPTION]... PROJECT [NAME]
@@ -22,22 +22,23 @@
#include "../core/print.h"
#include "../core/standard.h"
#include "contents.h"
#include "format.h"
#include "debug.h"
#include "format.h"
#define DEFAULT_USER_NAME "unknown"
#define DEFAULT_PROJECT_NAME "Project"
#define DEFAULT_LICENSE BSD3
#define DEFAULT_LICENCE BSD3
#define DEFAULT_GIT_INIT true
#define DEFAULT_CLANG_FORMAT true
/* This is to keep track of how deep we are within
the project tree. This is used in reset_path */
the project tree. This is used in reset_path_ () */
int depth;
#define print_option(option, description) \
printf (" %-20s %-20s\n", option, description)
// clang-format off
static void
usage (int status)
{
@@ -50,18 +51,20 @@ usage (int status)
printf ("Usage: yait [OPTION]... PROJECT [NAME]\n");
printf ("Creates a C project with opinionated defaults.\n");
printf ("When only given the first argument it will detect your name.\n\n");
printf ("Mandatory arguments to long options are mandatory for short "
"options too\n");
print_option ("-l, --license=NAME",
"Set license (gpl, mit, bsd) [default: bsd]");
printf ("Mandatory arguments to long options are mandatory for short options too\n");
print_option ("-l, --licence=NAME", "Set licence (gpl, mit, bsd) [default: bsd]");
print_option ("--lib=LIB", "Add a library to the project. You can list libraries with --lib=help.");
print_option ("--use-cpp", "Uses the CPP language instead of C");
print_option ("--git", "Initialize git repository");
print_option ("--GNU", "Adds standard GNU argument parsing to your project");
printf (" --help display this help text and exit\n");
printf (" --version output version information and exit\n");
}
// clang-format on
/* This macro exist purely because I like how it looks. */
/* This macro exist purely because I like how it looks. This should be called
in every function that creates file to ensure they are being created in
right place. */
#define reset_path reset_path_ ()
static int
reset_path_ ()
@@ -77,43 +80,78 @@ reset_path_ ()
}
static int
sanitize (format_t *fmt)
sanitize (manifest_t *m)
{
if (!fmt->project)
fmt->project = DEFAULT_PROJECT_NAME;
if (!fmt->name)
fmt->name = DEFAULT_USER_NAME;
if (fmt->license != BSD3 && fmt->license != GPLv3 && fmt->license != MIT)
fmt->license = DEFAULT_LICENSE;
fmt->flag.git = fmt->flag.git ? true : DEFAULT_GIT_INIT;
fmt->flag.clang_format
= fmt->flag.clang_format ? true : DEFAULT_CLANG_FORMAT;
fmt->flag.GNU = fmt->flag.GNU ? true : false;
if (!m->project)
m->project = DEFAULT_PROJECT_NAME;
if (!m->name)
m->name = DEFAULT_USER_NAME;
if (!(m->licence == UNLICENCE))
m->licence = DEFAULT_LICENCE;
m->flag.git = m->flag.git ? true : DEFAULT_GIT_INIT;
m->flag.clang_format = m->flag.clang_format ? true : DEFAULT_CLANG_FORMAT;
m->flag.GNU = m->flag.GNU ? true : DEFAULT_GNU;
return 0;
}
#define get(url) status = system ("git submodule add -q " url)
static int
create_license (format_t fmt, char **license_line_buffer)
create_libraries (manifest_t manifest)
{
if (fmt.license == UNlICENSE)
int status = 0;
if (!manifest.libraries)
{
return status;
}
reset_path;
for (int i = 0; i < LIB_COUNT_; ++i)
{
if HAS_LIBRARY (manifest.libraries, LIB_RAYLIB)
{
REMOVE_LIBRARY (manifest.libraries, LIB_RAYLIB);
get ("https://github.com/raysan5/raylib");
}
else if HAS_LIBRARY (manifest.libraries, LIB_NCURSES)
{
REMOVE_LIBRARY (manifest.libraries, LIB_NCURSES);
get ("https://github.com/mirror/ncurses");
}
else if HAS_LIBRARY (manifest.libraries, LIB_CURL)
{
REMOVE_LIBRARY (manifest.libraries, LIB_CURL);
get ("https://github.com/raysan5/raylib");
}
reset_path;
}
return status;
}
static int
create_licence (manifest_t manifest, char **licence_line_buffer)
{
if (manifest.licence == UNLICENCE)
return 0;
reset_path;
/* TODO: Run better checks on license_line_buffer to ensure we have enough
/* TODO: Run better checks on licence_line_buffer to ensure we have enough
space. This could be done through a multitude of ways; that is for you to
figure out. */
assert (license_line_buffer != NULL);
assert (licence_line_buffer != NULL);
// TODO: Remove this and actually implement the features.
#define TODO() \
printfn ("Not impl"); \
assert (1 == 2)
switch (fmt.license)
switch (manifest.licence)
{
case BSD3:
*license_line_buffer = "Bsd";
*licence_line_buffer = "Bsd";
TODO ();
break;
case GPLv3:
@@ -122,9 +160,9 @@ create_license (format_t fmt, char **license_line_buffer)
case MIT:
TODO ();
break;
case UNlICENSE:
case UNLICENCE:
default:
printfn ("bad logic in create_license_and_set_license_line()");
printfn ("bad logic in create_licence ()");
return 1;
}
@@ -132,36 +170,35 @@ create_license (format_t fmt, char **license_line_buffer)
}
static int
maybe_apply_clang_format (format_t fmt)
maybe_create_clang_format (manifest_t manifest)
{
if (!fmt.flag.clang_format)
if (!manifest.flag.clang_format)
return 0;
reset_path;
char *clang_fmt = "BasedOnStyle: LLVM\nIndentWidth: 2\nUseTab: Never\n";
return create_file_with_content (".clang-format", clang_fmt, 0, NULL);
return create_file_with_content (".clang-format", clang_format_template);
}
static int
setup_git (format_t fmt)
setup_git (manifest_t manifest)
{
if (!fmt.flag.git)
if (!manifest.flag.git)
return 0;
reset_path;
int err = system ("git init --quiet");
if (err)
printfn ("failed on git initialize: %s", strerror (err));
int status = system ("git init --quiet");
if (status)
printfn ("failed on git initialize: %s", strerror (status));
return err;
return status;
}
static int
create_makefile (format_t fmt)
create_makefile (manifest_t manifest)
{
char *makefile_name = strdup (fmt.project);
char *makefile_name = strdup (manifest.project);
if (!makefile_name)
{
printfn ("fatal: out of memory");
@@ -176,7 +213,7 @@ create_makefile (format_t fmt)
create_file_with_content ("Makefile", makefile_template, makefile_name,
makefile_name, makefile_name, makefile_name,
makefile_name, makefile_name, fmt.project,
makefile_name, makefile_name, manifest.project,
makefile_name, makefile_name);
free (makefile_name);
@@ -184,38 +221,51 @@ create_makefile (format_t fmt)
}
static int
create_configure ()
create_configure (manifest_t manifest)
{
int status = 0;
reset_path;
create_file_with_content ("configure", configure_template);
int err = system ("chmod +x configure");
if (err)
printfn ("error: %s", strerror (err));
return err;
char *cc;
if (manifest.flag.use_cpp)
{
cc = "trycc g++\ntrycc CC\ntrycc clang++\n";
}
else
{
cc = "trycc gcc\ntrycc cc\ntrycc clang\n";
}
create_file_with_content ("configure", configure_template, cc);
status = system ("chmod +x configure");
if (status)
printfn ("error: %s", strerror (status));
return status;
}
static int
generate_source_code (format_t fmt)
generate_source_code (manifest_t manifest)
{
int err;
int status;
debug ("take %s/%s", fmt.project, fmt.project);
err = create_and_enter_directory (fmt.project);
on_error ("failed to create or enter directory", err);
debug ("take %s/%s", manifest.project, manifest.project);
status = create_and_enter_directory (manifest.project);
on_error ("failed to create or enter directory", status);
++depth;
if (fmt.flag.GNU)
if (manifest.flag.GNU)
{
debug ("GNU flag source branch");
create_file_with_content ("main.c", main_c_gnu_template, fmt.project,
fmt.name);
create_file_with_content ("main.c", main_c_gnu_template,
manifest.project, manifest.name);
goto atexit_clean;
}
debug ("default sourcebranch");
create_file_with_content ("main.c", main_c_template, fmt.project, fmt.name);
create_file_with_content ("main.c", main_c_template, manifest.project,
manifest.name);
atexit_clean:
reset_path;
@@ -223,14 +273,13 @@ atexit_clean:
}
static int
parse_arguments (format_t *conf, int argc, char **argv)
parse_arguments (manifest_t *conf, int argc, char **argv)
{
static struct option long_options[]
= { { "GNU", no_argument, 0, 1 },
{ "use-cpp", no_argument, 0, 2 },
{ "git", no_argument, 0, 3 },
{ "license", required_argument, 0, 4 },
{ 0, 0, 0, 0 } };
static struct option long_options[] = {
{ "GNU", no_argument, 0, 1 }, { "use-cpp", no_argument, 0, 2 },
{ "git", no_argument, 0, 3 }, { "licence", required_argument, 0, 4 },
{ "lib", required_argument, 0, 5 }, { 0, 0, 0, 0 }
};
int opt;
int long_index = 0;
@@ -248,12 +297,16 @@ parse_arguments (format_t *conf, int argc, char **argv)
case 3:
conf->flag.git = 1;
break;
case 4: // TODO: Licence
case 4: // TODO: implement the licence options, and make it lowercase.
break;
case 5:
ADD_LIBRARY (conf->libraries, TOlibrary (optarg)); // TODO: Get this working
break;
case '?':
break;
}
}
int positional_count = 0;
for (int i = optind; i < argc; ++i)
{
@@ -264,56 +317,58 @@ parse_arguments (format_t *conf, int argc, char **argv)
}
if (positional_count == 0)
{
conf->project = argv[i];
}
else if (positional_count == 1)
{
conf->name = argv[i];
}
else if (positional_count == 1)
conf->project = argv[i];
++positional_count;
}
return 0;
}
static int
create_project (format_t fmt)
create_project (manifest_t manifest)
{
int err;
int status;
debugc ("sanitize");
err = sanitize (&fmt);
on_error ("failed to sanitize format", err);
status = sanitize (&manifest);
on_error ("failed to sanitize format", status);
done;
debugc ("take %s", fmt.project);
err = create_and_enter_directory (fmt.project);
debugc ("take %s", manifest.project);
status = create_and_enter_directory (manifest.project);
depth = 0;
on_error ("failed to create or enter directory", err);
on_error ("failed to create or enter directory", status);
done;
// debug ("create licenseing");
// err = create_license_if_needed (fmt);
// on_error ("failed to create license", err);
debugc ("create makefile");
err = create_makefile (fmt);
on_error ("failed to create Makefile", err);
status = create_makefile (manifest);
on_error ("failed to create Makefile", status);
done;
debug ("setup git");
err = setup_git (fmt);
if (err)
printfn ("warning: git initialization failed: %s", strerror (err));
status = setup_git (manifest);
if (status)
{
printfn ("warning: git initialization failed: %s", strerror (status));
}
debug ("create .clang-format");
err = maybe_apply_clang_format (fmt);
if (err)
printfn ("warning: clang-format setup failed: %s", strerror (err));
status = maybe_create_clang_format (manifest);
if (status)
{
printfn ("warning: clang-format setup failed: %s", strerror (status));
}
debugc ("generate source code");
err = generate_source_code (fmt);
on_error ("failed to generate source code", err);
status = generate_source_code (manifest);
on_error ("failed to generate source code", status);
done;
debugc ("get libraries");
status = create_libraries (manifest);
on_error ("failed to get libraries", status);
done;
return 0;
@@ -339,23 +394,18 @@ main (int argc, char **argv)
return status;
}
format_t conf = { 0 };
manifest_t manifest = { 0 };
// TODO: Argument Parsing
conf.project = "sample_project";
conf.name = "vx_clutch";
parse_arguments (&manifest, argc, argv);
if (!conf.name)
if (!manifest.name) // TODO: Move to sanitize
{
struct passwd *pw = getpwuid (getuid ());
conf.name = (pw && pw->pw_name) ? pw->pw_name : DEFAULT_USER_NAME;
manifest.name = (pw && pw->pw_name) ? pw->pw_name : DEFAULT_USER_NAME;
}
conf.flag.git = DEFAULT_GIT_INIT;
conf.flag.clang_format = DEFAULT_CLANG_FORMAT;
conf.license = DEFAULT_LICENSE;
status = create_project (manifest);
status = create_project (conf);
if (!status)
debug ("project made successfully");
else