save
This commit is contained in:
6
TODO
6
TODO
@@ -1,6 +0,0 @@
|
|||||||
1 Fix arg parseing
|
|
||||||
2 Fix generated Makefile
|
|
||||||
3 Add Licence
|
|
||||||
|
|
||||||
Ideas
|
|
||||||
1 Add --is-lib
|
|
||||||
3
config.h
3
config.h
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
/* Program information */
|
/* Program information */
|
||||||
#define PROGRAM "yait"
|
#define PROGRAM "yait"
|
||||||
#define LICENSE_LINE "License BSD-3-Clause: BSD-3-Clause <https://opensource.org/license/bsd-3-clause>"
|
#define LICENSE_LINE \
|
||||||
|
"License BSD-3-Clause: BSD-3-Clause <https://opensource.org/license/bsd-3-clause>"
|
||||||
#define AUTHORS "vx_clutch"
|
#define AUTHORS "vx_clutch"
|
||||||
#define VERSION "alpha"
|
#define VERSION "alpha"
|
||||||
#define YEAR 2025
|
#define YEAR 2025
|
||||||
|
|||||||
346
core/create_project.c
Normal file
346
core/create_project.c
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
|
#include "manifest.h"
|
||||||
|
#include "contents.h"
|
||||||
|
#include "yait.h"
|
||||||
|
|
||||||
|
#define DEFAULT_USER_NAME "unknown"
|
||||||
|
#define DEFAULT_PROJECT_NAME "Project"
|
||||||
|
#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_ () */
|
||||||
|
int depth;
|
||||||
|
|
||||||
|
/* 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_()
|
||||||
|
int reset_path_()
|
||||||
|
{
|
||||||
|
while (depth != 0) {
|
||||||
|
if (chdir("..") != 0)
|
||||||
|
return errno;
|
||||||
|
else
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int program_exists(const char *prog)
|
||||||
|
{
|
||||||
|
char *path = getenv("PATH");
|
||||||
|
if (!path)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
char *copy = strdup(path);
|
||||||
|
if (!copy)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
char *dir = strtok(copy, ":");
|
||||||
|
while (dir) {
|
||||||
|
char buf[4096];
|
||||||
|
snprintf(buf, sizeof(buf), "%s/%s", dir, prog);
|
||||||
|
if (access(buf, X_OK) == 0) {
|
||||||
|
free(copy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dir = strtok(NULL, ":");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(copy);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(vx-clutch): sanitize the alpha-numeric range
|
||||||
|
// clang-format off
|
||||||
|
int sanitize(manifest_t *m)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!m->name) {
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
m->name = (pw && pw->pw_name) ? pw->pw_name : DEFAULT_USER_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
int create_libraries(manifest_t manifest)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
|
if (!manifest.libraries) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset_path; */
|
||||||
|
while (depth != 0) {
|
||||||
|
if (chdir("..") != 0)
|
||||||
|
return errno;
|
||||||
|
else
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < LIB_COUNT_; ++i) {
|
||||||
|
if (HAS_LIBRARY(manifest.libraries, LIB_RAYLIB)) {
|
||||||
|
REMOVE_LIBRARY(manifest.libraries, LIB_RAYLIB);
|
||||||
|
status = system(
|
||||||
|
"git submodule add -q https://github.com/raysan5/raylib");
|
||||||
|
} else if (HAS_LIBRARY(manifest.libraries, LIB_NCURSES)) {
|
||||||
|
REMOVE_LIBRARY(manifest.libraries, LIB_NCURSES);
|
||||||
|
status = system(
|
||||||
|
"git submodule add -q https://github.com/mirror/ncurses");
|
||||||
|
} else if (HAS_LIBRARY(manifest.libraries, LIB_CURL)) {
|
||||||
|
REMOVE_LIBRARY(manifest.libraries, LIB_CURL);
|
||||||
|
status = system(
|
||||||
|
"git submodule add -q https://github.com/raysan5/raylib");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_licence(manifest_t manifest, char **licence_line_buffer)
|
||||||
|
{
|
||||||
|
if (manifest.licence == UNLICENCE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* reset_path; */
|
||||||
|
while (depth != 0) {
|
||||||
|
if (chdir("..") != 0)
|
||||||
|
return errno;
|
||||||
|
else
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(licence_line_buffer != NULL);
|
||||||
|
|
||||||
|
switch (manifest.licence) {
|
||||||
|
case BSD3:
|
||||||
|
*licence_line_buffer = "Bsd";
|
||||||
|
printfn("Not impl");
|
||||||
|
assert(1 == 2);
|
||||||
|
break;
|
||||||
|
case GPLv3:
|
||||||
|
printfn("Not impl");
|
||||||
|
assert(1 == 2);
|
||||||
|
break;
|
||||||
|
case MIT:
|
||||||
|
printfn("Not impl");
|
||||||
|
assert(1 == 2);
|
||||||
|
break;
|
||||||
|
case UNLICENCE:
|
||||||
|
default:
|
||||||
|
printfn("bad logic in create_licence ()");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maybe_create_clang_format(manifest_t manifest)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
if (!manifest.flag.clang_format)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* reset_path; */
|
||||||
|
while (depth != 0) {
|
||||||
|
if (chdir("..") != 0)
|
||||||
|
return errno;
|
||||||
|
else
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = create_file_with_content(".clang-format",
|
||||||
|
clang_format_template);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int setup_git(manifest_t manifest)
|
||||||
|
{
|
||||||
|
if (!manifest.flag.git) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset_path; */
|
||||||
|
while (depth != 0) {
|
||||||
|
if (chdir("..") != 0)
|
||||||
|
return errno;
|
||||||
|
else
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = system("git init --quiet");
|
||||||
|
if (status) {
|
||||||
|
printfn("failed on git initialize: %s", strerror(status));
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_makefile(manifest_t manifest)
|
||||||
|
{
|
||||||
|
char *makefile_name = strdup(manifest.project);
|
||||||
|
if (!makefile_name) {
|
||||||
|
printfn("fatal: out of memory");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (char *p = makefile_name; *p; ++p)
|
||||||
|
if (*p >= 'a' && *p <= 'z')
|
||||||
|
*p -= 32;
|
||||||
|
|
||||||
|
reset_path;
|
||||||
|
|
||||||
|
create_file_with_content("Makefile", makefile_template, makefile_name,
|
||||||
|
makefile_name, makefile_name, makefile_name,
|
||||||
|
makefile_name, makefile_name, manifest.project,
|
||||||
|
makefile_name, makefile_name);
|
||||||
|
|
||||||
|
free(makefile_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_configure(manifest_t manifest)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
reset_path;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int generate_source_code(manifest_t manifest, char *licence_line)
|
||||||
|
{
|
||||||
|
int status, year;
|
||||||
|
|
||||||
|
time_t t = time(NULL);
|
||||||
|
struct tm tm = *localtime(&t);
|
||||||
|
year = tm.tm_year + 1900;
|
||||||
|
|
||||||
|
// XXX(vx-clutch): this segfaults, but why?
|
||||||
|
// status = create_file_with_content("config.h", manifest.project, licence_line, year);
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to create config.h: %s", strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = create_and_enter_directory(manifest.project);
|
||||||
|
++depth;
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to create or enter directory: %s",
|
||||||
|
strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manifest.flag.GNU) {
|
||||||
|
status = create_file_with_content("main.c", main_c_gnu_template,
|
||||||
|
manifest.project,
|
||||||
|
manifest.name);
|
||||||
|
} else {
|
||||||
|
status = create_file_with_content("main.c", main_c_template,
|
||||||
|
manifest.project,
|
||||||
|
manifest.name);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_project(manifest_t manifest)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = sanitize(&manifest);
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to sanitize format: %s", strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
depth = 0;
|
||||||
|
if (strcmp(manifest.path, ".") != 0) {
|
||||||
|
status = create_and_enter_directory(manifest.project);
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to create or enter directory: %s",
|
||||||
|
strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status = create_makefile(manifest);
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to create Makefile: %s", strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = create_configure(manifest);
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to create configure: %s", strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = setup_git(manifest);
|
||||||
|
if (status) {
|
||||||
|
printfn("warning: git initialization failed: %s",
|
||||||
|
strerror(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
status = maybe_create_clang_format(manifest);
|
||||||
|
if (status) {
|
||||||
|
printfn("warning: clang-format setup failed: %s",
|
||||||
|
strerror(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(vx-clutch): make this smarter--or not ( macro ).
|
||||||
|
char *licence_line = malloc(sizeof(char) * 1024);
|
||||||
|
if (!licence_line) {
|
||||||
|
printfn("failed to create memory for licence line: %s",
|
||||||
|
strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
status = create_licence(manifest, &licence_line);
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to get libraries: %s", strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(vx-clutch): Take in licence line and put it into standard.c
|
||||||
|
status = generate_source_code(manifest, licence_line);
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to generate source code: %s", strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
free(licence_line);
|
||||||
|
|
||||||
|
status = create_libraries(manifest);
|
||||||
|
if (status) {
|
||||||
|
printfn("failed to get libraries: %s", strerror(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -6,13 +6,14 @@
|
|||||||
* <https://opensource.org/license/bsd-3-clause>
|
* <https://opensource.org/license/bsd-3-clause>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "file.h"
|
|
||||||
#include "../config.h"
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "yait.h"
|
||||||
|
|
||||||
int create_file_with_content(char *path, char *format, ...)
|
int create_file_with_content(char *path, char *format, ...)
|
||||||
{
|
{
|
||||||
|
|||||||
24
core/file.h
24
core/file.h
@@ -1,24 +0,0 @@
|
|||||||
/* Copyright (C) vx_clutch
|
|
||||||
*
|
|
||||||
* 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>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FILE_H
|
|
||||||
#define FILE_H
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
/* Constants for file operations */
|
|
||||||
#define DEFAULT_DIR_PERMISSIONS 0755
|
|
||||||
#define MAX_PATH_LENGTH 1024
|
|
||||||
|
|
||||||
int create_and_enter_directory (const char *dirname);
|
|
||||||
|
|
||||||
int create_file_with_content (char *path, char *format, ...);
|
|
||||||
|
|
||||||
int create_directory (char *format, ...);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
78
core/manifest.h
Normal file
78
core/manifest.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/* Copyright (C) vx_clutch
|
||||||
|
*
|
||||||
|
* This file is part of yait
|
||||||
|
*
|
||||||
|
* This project and file is licenced under the BSD-3-Clause licence.
|
||||||
|
* <https://opensource.org/licence/bsd-3-clause>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FORMAT_H
|
||||||
|
#define FORMAT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSD3, /* BSD 3-Clause Licence */
|
||||||
|
GPLv3, /* GNU General Public Licence v3 */
|
||||||
|
MIT, /* MIT Licence */
|
||||||
|
UNLICENCE, /* Unlicence */
|
||||||
|
LICENCE_HELP, /* Help case */
|
||||||
|
} licence_t;
|
||||||
|
|
||||||
|
/* 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_NCURSES = 1 << 1, /* Windows API */
|
||||||
|
LIB_CURL = 1 << 2, /* cURL library */
|
||||||
|
LIB_COUNT_, /* Number of Libraries */
|
||||||
|
LIB_HELP, /* Help case */
|
||||||
|
} lib_flags_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool GNU;
|
||||||
|
bool git;
|
||||||
|
bool clang_format;
|
||||||
|
bool use_cpp;
|
||||||
|
} flags_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
licence_t licence; /* Licence type for the project */
|
||||||
|
char *project; /* Project name */
|
||||||
|
char *path; /* Path */
|
||||||
|
char *name; /* Author/creator name ( if not provided infered on sanitize ) */
|
||||||
|
lib_flags_t libraries; /* Selected libraries (bit field) */
|
||||||
|
flags_t flag; /* Flags */
|
||||||
|
} manifest_t;
|
||||||
|
|
||||||
|
#define DEFAULT_CLANG_FORMAT true
|
||||||
|
#define DEFAULT_GIT_INIT true
|
||||||
|
#define DEFAULT_GNU false
|
||||||
|
#define DEFAULT_LIBRARIES LIB_NONE
|
||||||
|
#define DEFAULT_LICENCE BSD3
|
||||||
|
|
||||||
|
#define HAS_LIBRARY(libs, lib) ((libs) & (lib))
|
||||||
|
#define ADD_LIBRARY(libs, lib) ((libs) |= (lib))
|
||||||
|
#define REMOVE_LIBRARY(libs, lib) ((libs) &= ~(lib))
|
||||||
|
|
||||||
|
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;
|
||||||
|
fprintf(stderr, "could not find library %s", src);
|
||||||
|
return LIB_COUNT_; /* bad case */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
* <https://opensource.org/license/bsd-3-clause>
|
* <https://opensource.org/license/bsd-3-clause>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "print.h"
|
#include "yait.h"
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|||||||
17
core/print.h
17
core/print.h
@@ -1,17 +0,0 @@
|
|||||||
/* Copyright (C) vx_clutch
|
|
||||||
*
|
|
||||||
* 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>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PRINT_H
|
|
||||||
#define PRINT_H
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int printfn (char *format, ...);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
#ifndef COMMIT
|
#ifndef COMMIT
|
||||||
#define COMMIT 0
|
#define COMMIT 0
|
||||||
#endif
|
#endif
|
||||||
#include "standard.h"
|
|
||||||
|
#include "yait.h"
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
/* Copyright (C) vx_clutch
|
|
||||||
*
|
|
||||||
* 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>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef STANDARD_H
|
|
||||||
#define STANDARD_H
|
|
||||||
|
|
||||||
int parse_standard_options (void (*usage_func) (), int argc, char **argv);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
31
core/yait.h
Normal file
31
core/yait.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/* Copyright (C) vx_clutch
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef YAIT_H
|
||||||
|
#define YAIT_H
|
||||||
|
|
||||||
|
/* Constants for file operations */
|
||||||
|
#define DEFAULT_DIR_PERMISSIONS 0755
|
||||||
|
#define MAX_PATH_LENGTH 1024
|
||||||
|
|
||||||
|
int printfn(char *format, ...);
|
||||||
|
|
||||||
|
int create_and_enter_directory(const char *dirname);
|
||||||
|
|
||||||
|
int create_file_with_content(char *path, char *format, ...);
|
||||||
|
|
||||||
|
int create_directory(char *format, ...);
|
||||||
|
|
||||||
|
int parse_standard_options(void (*usage_func)(), int argc, char **argv);
|
||||||
|
|
||||||
|
int program_exists(const char *prog);
|
||||||
|
|
||||||
|
#include "manifest.h"
|
||||||
|
int create_project(manifest_t manifest);
|
||||||
|
|
||||||
|
#endif // YAIT_H
|
||||||
@@ -3,3 +3,4 @@
|
|||||||
# Usage ./format
|
# Usage ./format
|
||||||
|
|
||||||
find . -name "*.c" -exec clang-format -i {} \;
|
find . -name "*.c" -exec clang-format -i {} \;
|
||||||
|
find . -name "*.h" -exec clang-format -i {} \;
|
||||||
|
|||||||
368
yait/main.c
368
yait/main.c
@@ -6,34 +6,18 @@
|
|||||||
* <https://opensource.org/licence/bsd-3-clause>
|
* <https://opensource.org/licence/bsd-3-clause>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Usage: yait [OPTION]... PROJECT [NAME]
|
// Usage: yait [OPTION]... <PATH>
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
#include "../core/file.h"
|
#include "../core/yait.h"
|
||||||
#include "../core/print.h"
|
#include "../core/manifest.h"
|
||||||
#include "../core/standard.h"
|
|
||||||
#include "contents.h"
|
|
||||||
#include "manifest.h"
|
|
||||||
|
|
||||||
#define DEFAULT_USER_NAME "unknown"
|
|
||||||
#define DEFAULT_PROJECT_NAME "Project"
|
|
||||||
#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_ () */
|
|
||||||
int depth;
|
|
||||||
|
|
||||||
#define print_option(option, description) \
|
#define print_option(option, description) \
|
||||||
printf(" %-20s %-20s\n", option, description)
|
printf(" %-20s %-20s\n", option, description)
|
||||||
@@ -115,325 +99,30 @@ static int parse_arguments(manifest_t *conf, int argc, char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This macro exist purely because I like how it looks. This should be called
|
int get_name(char **output)
|
||||||
in every function that creates file to ensure they are being created in
|
|
||||||
right place. */
|
|
||||||
#define reset_path reset_path_()
|
|
||||||
static int reset_path_()
|
|
||||||
{
|
{
|
||||||
while (depth != 0) {
|
FILE *pipe;
|
||||||
if (chdir("..") != 0)
|
char buffer[128];
|
||||||
return errno;
|
size_t output_len = 0;
|
||||||
else
|
|
||||||
--depth;
|
pipe = popen("ls -l", "r");
|
||||||
|
if (!pipe)
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
|
||||||
|
size_t chunk_len = strlen(buffer);
|
||||||
|
char *new_output = realloc(output, output_len + chunk_len + 1);
|
||||||
|
if (!new_output) {
|
||||||
|
free(output);
|
||||||
|
pclose(pipe);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
return 0;
|
*output = new_output;
|
||||||
|
memcpy(output + output_len, buffer, chunk_len);
|
||||||
|
output_len += chunk_len;
|
||||||
|
*output[output_len] = '\0';
|
||||||
}
|
}
|
||||||
|
pclose(pipe);
|
||||||
int program_exists(const char *prog)
|
|
||||||
{
|
|
||||||
char *path = getenv("PATH");
|
|
||||||
if (!path)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
char *copy = strdup(path);
|
|
||||||
if (!copy)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
char *dir = strtok(copy, ":");
|
|
||||||
while (dir) {
|
|
||||||
char buf[4096];
|
|
||||||
snprintf(buf, sizeof(buf), "%s/%s", dir, prog);
|
|
||||||
if (access(buf, X_OK) == 0) {
|
|
||||||
free(copy);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
dir = strtok(NULL, ":");
|
|
||||||
}
|
|
||||||
|
|
||||||
free(copy);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(vx-clutch): sanitize the alpha-numeric range
|
|
||||||
// clang-format off
|
|
||||||
static int sanitize(manifest_t *m)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (!m->name) {
|
|
||||||
struct passwd *pw = getpwuid(getuid());
|
|
||||||
m->name = (pw && pw->pw_name) ? pw->pw_name : DEFAULT_USER_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
static int create_libraries(manifest_t manifest)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
if (!manifest.libraries) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset_path; */
|
|
||||||
while (depth != 0) {
|
|
||||||
if (chdir("..") != 0)
|
|
||||||
return errno;
|
|
||||||
else
|
|
||||||
--depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < LIB_COUNT_; ++i) {
|
|
||||||
if (HAS_LIBRARY(manifest.libraries, LIB_RAYLIB)) {
|
|
||||||
REMOVE_LIBRARY(manifest.libraries, LIB_RAYLIB);
|
|
||||||
status = system(
|
|
||||||
"git submodule add -q https://github.com/raysan5/raylib");
|
|
||||||
} else if (HAS_LIBRARY(manifest.libraries, LIB_NCURSES)) {
|
|
||||||
REMOVE_LIBRARY(manifest.libraries, LIB_NCURSES);
|
|
||||||
status = system(
|
|
||||||
"git submodule add -q https://github.com/mirror/ncurses");
|
|
||||||
} else if (HAS_LIBRARY(manifest.libraries, LIB_CURL)) {
|
|
||||||
REMOVE_LIBRARY(manifest.libraries, LIB_CURL);
|
|
||||||
status = system(
|
|
||||||
"git submodule add -q https://github.com/raysan5/raylib");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_licence(manifest_t manifest, char **licence_line_buffer)
|
|
||||||
{
|
|
||||||
if (manifest.licence == UNLICENCE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* reset_path; */
|
|
||||||
while (depth != 0) {
|
|
||||||
if (chdir("..") != 0)
|
|
||||||
return errno;
|
|
||||||
else
|
|
||||||
--depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(licence_line_buffer != NULL);
|
|
||||||
|
|
||||||
switch (manifest.licence) {
|
|
||||||
case BSD3:
|
|
||||||
*licence_line_buffer = "Bsd";
|
|
||||||
printfn("Not impl");
|
|
||||||
assert(1 == 2);
|
|
||||||
break;
|
|
||||||
case GPLv3:
|
|
||||||
printfn("Not impl");
|
|
||||||
assert(1 == 2);
|
|
||||||
break;
|
|
||||||
case MIT:
|
|
||||||
printfn("Not impl");
|
|
||||||
assert(1 == 2);
|
|
||||||
break;
|
|
||||||
case UNLICENCE:
|
|
||||||
default:
|
|
||||||
printfn("bad logic in create_licence ()");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int maybe_create_clang_format(manifest_t manifest)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
if (!manifest.flag.clang_format)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* reset_path; */
|
|
||||||
while (depth != 0) {
|
|
||||||
if (chdir("..") != 0)
|
|
||||||
return errno;
|
|
||||||
else
|
|
||||||
--depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = create_file_with_content(".clang-format",
|
|
||||||
clang_format_template);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int setup_git(manifest_t manifest)
|
|
||||||
{
|
|
||||||
if (!manifest.flag.git) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset_path; */
|
|
||||||
while (depth != 0) {
|
|
||||||
if (chdir("..") != 0)
|
|
||||||
return errno;
|
|
||||||
else
|
|
||||||
--depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
int status = system("git init --quiet");
|
|
||||||
if (status) {
|
|
||||||
printfn("failed on git initialize: %s", strerror(status));
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_makefile(manifest_t manifest)
|
|
||||||
{
|
|
||||||
char *makefile_name = strdup(manifest.project);
|
|
||||||
if (!makefile_name) {
|
|
||||||
printfn("fatal: out of memory");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (char *p = makefile_name; *p; ++p)
|
|
||||||
if (*p >= 'a' && *p <= 'z')
|
|
||||||
*p -= 32;
|
|
||||||
|
|
||||||
reset_path;
|
|
||||||
|
|
||||||
create_file_with_content("Makefile", makefile_template, makefile_name,
|
|
||||||
makefile_name, makefile_name, makefile_name,
|
|
||||||
makefile_name, makefile_name, manifest.project,
|
|
||||||
makefile_name, makefile_name);
|
|
||||||
|
|
||||||
free(makefile_name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_configure(manifest_t manifest)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
reset_path;
|
|
||||||
|
|
||||||
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(manifest_t manifest, char *licence_line)
|
|
||||||
{
|
|
||||||
int status, year;
|
|
||||||
|
|
||||||
time_t t = time(NULL);
|
|
||||||
struct tm tm = *localtime(&t);
|
|
||||||
year = tm.tm_year + 1900;
|
|
||||||
|
|
||||||
// XXX(vx-clutch): this segfaults, but why?
|
|
||||||
// status = create_file_with_content("config.h", manifest.project, licence_line, year);
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to create config.h: %s", strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = create_and_enter_directory(manifest.project);
|
|
||||||
++depth;
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to create or enter directory: %s",
|
|
||||||
strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (manifest.flag.GNU) {
|
|
||||||
status = create_file_with_content("main.c", main_c_gnu_template,
|
|
||||||
manifest.project,
|
|
||||||
manifest.name);
|
|
||||||
} else {
|
|
||||||
status = create_file_with_content("main.c", main_c_template,
|
|
||||||
manifest.project,
|
|
||||||
manifest.name);
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_project(manifest_t manifest)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
|
|
||||||
status = sanitize(&manifest);
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to sanitize format: %s", strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = create_and_enter_directory(manifest.project);
|
|
||||||
depth = 0;
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to create or enter directory: %s",
|
|
||||||
strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = create_makefile(manifest);
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to create Makefile: %s", strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = create_configure(manifest);
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to create configure: %s", strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = setup_git(manifest);
|
|
||||||
if (status) {
|
|
||||||
printfn("warning: git initialization failed: %s",
|
|
||||||
strerror(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
status = maybe_create_clang_format(manifest);
|
|
||||||
if (status) {
|
|
||||||
printfn("warning: clang-format setup failed: %s",
|
|
||||||
strerror(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(vx-clutch): make this smarter--or not ( macro ).
|
|
||||||
char *licence_line = malloc(sizeof(char) * 1024);
|
|
||||||
if (!licence_line) {
|
|
||||||
printfn("failed to create memory for licence line: %s",
|
|
||||||
strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
status = create_licence(manifest, &licence_line);
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to get libraries: %s", strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(vx-clutch): Take in licence line and put it into standard.c
|
|
||||||
status = generate_source_code(manifest, licence_line);
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to generate source code: %s", strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
free(licence_line);
|
|
||||||
|
|
||||||
status = create_libraries(manifest);
|
|
||||||
if (status) {
|
|
||||||
printfn("failed to get libraries: %s", strerror(status));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,12 +143,9 @@ int main(int argc, char **argv)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(vx-clutch): enable argument parsing
|
status = get_name(&manifest.name);
|
||||||
// parse_arguments(&manifest, argc, argv);
|
manifest.path = ".";
|
||||||
manifest.project = "sample_project";
|
|
||||||
manifest.name = "vx_clutch";
|
|
||||||
manifest.licence = BSD3;
|
|
||||||
manifest.libraries = ADD_LIBRARY(manifest.libraries, LIB_NONE);
|
|
||||||
status = create_project(manifest);
|
status = create_project(manifest);
|
||||||
|
|
||||||
return status == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
return status == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
/* Copyright (C) vx_clutch
|
|
||||||
*
|
|
||||||
* This file is part of yait
|
|
||||||
*
|
|
||||||
* This project and file is licenced under the BSD-3-Clause licence.
|
|
||||||
* <https://opensource.org/licence/bsd-3-clause>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FORMAT_H
|
|
||||||
#define FORMAT_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "../core/print.h"
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
BSD3, /* BSD 3-Clause Licence */
|
|
||||||
GPLv3, /* GNU General Public Licence v3 */
|
|
||||||
MIT, /* MIT Licence */
|
|
||||||
UNLICENCE, /* Unlicence */
|
|
||||||
LICENCE_HELP, /* Help case */
|
|
||||||
} licence_t;
|
|
||||||
|
|
||||||
/* 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_NCURSES = 1 << 1, /* Windows API */
|
|
||||||
LIB_CURL = 1 << 2, /* cURL library */
|
|
||||||
LIB_COUNT_, /* Number of Libraries */
|
|
||||||
LIB_HELP, /* Help case */
|
|
||||||
} lib_flags_t;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
bool GNU;
|
|
||||||
bool git;
|
|
||||||
bool clang_format;
|
|
||||||
bool use_cpp;
|
|
||||||
} flags_t;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
licence_t licence; /* Licence type for the project */
|
|
||||||
char *project; /* Project name */
|
|
||||||
char *name; /* Author/creator name ( if not provided infered on sanitize ) */
|
|
||||||
lib_flags_t libraries; /* Selected libraries (bit field) */
|
|
||||||
flags_t flag; /* Flags */
|
|
||||||
} manifest_t;
|
|
||||||
|
|
||||||
#define DEFAULT_CLANG_FORMAT true
|
|
||||||
#define DEFAULT_GIT_INIT true
|
|
||||||
#define DEFAULT_GNU false
|
|
||||||
#define DEFAULT_LIBRARIES LIB_NONE
|
|
||||||
#define DEFAULT_LICENCE BSD3
|
|
||||||
|
|
||||||
#define HAS_LIBRARY(libs, lib) ((libs) & (lib))
|
|
||||||
#define ADD_LIBRARY(libs, lib) ((libs) |= (lib))
|
|
||||||
#define REMOVE_LIBRARY(libs, lib) ((libs) &= ~(lib))
|
|
||||||
|
|
||||||
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;
|
|
||||||
printfn("could not find library %s", src);
|
|
||||||
return LIB_COUNT_; /* bad case */
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user