Skip to content

Commit

Permalink
Generate values in C-compatibility headers by compiling C values.
Browse files Browse the repository at this point in the history
  • Loading branch information
asoffer committed Nov 18, 2023
1 parent ab38d3c commit 02e89ba
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 30 deletions.
30 changes: 19 additions & 11 deletions toolchain/stdlib/compat/c/BUILD
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
load("//toolchain:ic_rule.bzl", "ic_library")
load("//toolchain/stdlib/compat/c/internal:generate.bzl", "generated_ic_library")

package(default_visibility = ["//visibility:public"])

genrule(
name = "generate_c_types",
outs = ["types.ic"],
cmd = "./$(location //toolchain/stdlib/compat/c/internal:c_types_generator) > $@",
tools = ["//toolchain/stdlib/compat/c/internal:c_types_generator"],
visibility = ["//visibility:private"],
)

ic_library(
name = "types",
srcs = [":generate_c_types"],
Expand All @@ -33,8 +26,23 @@ ic_library(
deps = [":types"],
)

ic_library(
genrule(
name = "generate_c_types",
outs = ["types.ic"],
cmd = "./$(location //toolchain/stdlib/compat/c/internal:c_types_generator) > $@",
tools = ["//toolchain/stdlib/compat/c/internal:c_types_generator"],
visibility = ["//visibility:private"],
)

generated_ic_library(
name = "time",
srcs = ["time.ic"],
deps = [":types"],
srcs = ["time.template.ic"],
includes = ["time.h"],
symbols = {
"CLOCKS_PER_SEC": "int",
"TIME_UTC": "int",
},
deps = [
":types",
],
)
10 changes: 10 additions & 0 deletions toolchain/stdlib/compat/c/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,13 @@ cc_binary(
name = "c_types_generator",
srcs = ["c_types_generator.cc"],
)

cc_binary(
name = "generate",
srcs = ["generate.cc"],
)

cc_binary(
name = "generate_c_values",
srcs = ["generate_c_values.cc"],
)
51 changes: 51 additions & 0 deletions toolchain/stdlib/compat/c/internal/generate.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
load("//toolchain:ic_rule.bzl", "ic_library")

def generated_ic_library(name, **kwargs):
if len(kwargs["srcs"]) != 1:
fail("Must provide exactly one source template.")
return

native.genrule(
name = "internal_generate_c_values_{}_src".format(name),
outs = ["internal_generate_c_values_{}_src.cc".format(name)],
cmd = "./$(location //toolchain/stdlib/compat/c/internal:generate_c_values) \"{}\" {} > $@".format(
"".join(["#include <{}>\n".format(i) for i in kwargs["includes"]]),
" ".join(["{} {}".format(*kv) for kv in kwargs["symbols"].items()])
),
tools = ["//toolchain/stdlib/compat/c/internal:generate_c_values"],
visibility = ["//visibility:private"],
)

native.cc_binary(
name = "internal_generate_c_values_{}_bin".format(name),
srcs = [":internal_generate_c_values_{}_src".format(name)],
visibility = ["//visibility:private"],
)

native.genrule(
name = "internal_generate_c_values_{}_gen".format(name),
outs = ["internal_generate_c_values_{}".format(name)],
cmd = "./$(location //toolchain/stdlib/compat/c:internal_generate_c_values_{}_bin) > $@".format(
name
),
tools = ["//toolchain/stdlib/compat/c:internal_generate_c_values_{}_bin".format(name)],
visibility = ["//visibility:private"],
)

native.genrule(
name = "internal_generate_{}".format(name),
outs = ["{}.ic".format(name)],
srcs = kwargs["srcs"] + [":internal_generate_c_values_{}_gen".format(name)],
cmd = "./$(location //toolchain/stdlib/compat/c/internal:generate) $(location {}) $(location {}) > $@".format(
kwargs["srcs"][0],
":internal_generate_c_values_{}_gen".format(name),
),
tools = ["//toolchain/stdlib/compat/c/internal:generate"],
visibility = ["//visibility:private"],
)

ic_library(
name = name,
srcs = [":internal_generate_{}".format(name)],
deps = kwargs["deps"],
)
63 changes: 63 additions & 0 deletions toolchain/stdlib/compat/c/internal/generate.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include <cstdio>
#include <string>
#include <type_traits>

struct string_view {
char const * ptr;
size_t length;
};

string_view FindReplacement(std::string const &s, std::string const & needle) {
size_t index = s.find(needle);
if (index == std::string::npos) { std::abort(); }
if (index + needle.size() + 1 >= s.size()) { std::abort(); }
if (s[index + needle.size()] != '\t') { std::abort(); }
size_t start = index + needle.size() + 1;
size_t end = s.find("\n", start);
if (end == std::string::npos) { std::abort(); }
string_view sv;
sv.ptr = s.data() + start;
sv.length = end - start;
return sv;
}

bool ReadFileToString(char const *file_name, std::string &result) {
auto *f = std::fopen(file_name, "r");
if (not f) { return false; }
std::fseek(f, 0, SEEK_END);
size_t file_size = std::ftell(f);
std::rewind(f);
result.clear();
result.resize(file_size, '\0');
size_t count = std::fread(&result[0], 1, result.size(), f);
(void)count;
std::fclose(f);
return true;
}

int main(int argc, char const *argv[]) {
std::string content, replacements;
std::printf("// Generated from %s.\n", argv[1]);
if (not ReadFileToString(argv[1], content)) { return 1; }
if (not ReadFileToString(argv[2], replacements)) { return 1; }

while (true) {
auto index = content.find("{{{");
if (index == std::string::npos) {
std::printf("%*s", static_cast<int>(content.size()), content.data());
break;
}

auto end_index = content.find("}}}", index);
if (end_index == std::string::npos) { std::abort(); }

string_view s = FindReplacement(
replacements, content.substr(index + 3, end_index - index - 3));
std::printf("%.*s%.*s", static_cast<int>(index), content.data(),
static_cast<int>(s.length), s.ptr);

content = content.substr(end_index + 3);
}

return 0;
}
25 changes: 25 additions & 0 deletions toolchain/stdlib/compat/c/internal/generate_c_values.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>

char const *FormatSpecifier(char const *type) {
if (std::strcmp(type, "int") == 0) { return "d"; }
if (std::strcmp(type, "char") == 0) { return "c"; }
if (std::strcmp(type, "double") == 0) { return "f"; }
if (std::strcmp(type, "float") == 0) { return "f"; }
std::abort();
}

int main(int argc, char const *argv[]) {
std::puts("#include <cstdio>");
std::puts(argv[1]);
std::puts("int main() {");
for (int i = 2; i < argc; i += 2) {
std::printf(" std::printf(\"%s\\t%%%s\\n\", static_cast<%s>(%s));\n",
argv[i], FormatSpecifier(argv[i + 1]), argv[i + 1], argv[i]);
}
std::puts(" return 0;\n}");

return 0;
}

19 changes: 0 additions & 19 deletions toolchain/stdlib/compat/c/time.ic

This file was deleted.

27 changes: 27 additions & 0 deletions toolchain/stdlib/compat/c/time.template.ic
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This module provides an interface to functions in the <time.h> C-header file.

let c ::= import "std.compat.c.types"

let clock_t ::= c.long
let time_t ::= c.long
let tm ::= builtin.opaque()
let timespec ::= builtin.opaque()
let size_t ::= c.unsigned_long // TODO: generate this type.

let CLOCKS_PER_SEC :: clock_t = {{{CLOCKS_PER_SEC}}}
let TIME_UTC ::= {{{TIME_UTC}}}

let clock ::= builtin.foreign("clock", () -> clock_t)
let difftime ::= builtin.foreign("difftime", (time_t, time_t) -> c.double)
let mktime ::= builtin.foreign("mktime", *tm -> time_t)
let time ::= builtin.foreign("time", *time_t -> time_t)
let timespec_get ::= builtin.foreign("timespec_get", (*timespec, c.int) -> c.int)
let asctime ::= builtin.foreign("asctime", *tm -> [*]char)
let ctime ::= builtin.foreign("ctime", *time_t -> [*]char)
let gmtime ::= builtin.foreign("gmtime", *time_t -> *tm)
let localtime ::= builtin.foreign("localtime", *time_t -> *tm)
let strftime ::= builtin.foreign("strftime", ([*]char, size_t, [*]char, *tm) -> size_t)

// Additionally, the <time.h> C-header defines the constant `NULL`, for which
// there is no corresponding value in this module. For such uses, prefer the
// Icarus native `null` constant.

0 comments on commit 02e89ba

Please sign in to comment.