2021-02-02 15:41:51 +01:00
|
|
|
module c
|
2021-01-14 15:20:11 +01:00
|
|
|
|
|
|
|
import os
|
2021-12-04 18:43:19 +01:00
|
|
|
import rand
|
2021-01-14 15:20:11 +01:00
|
|
|
import v.ast
|
2021-12-04 18:43:19 +01:00
|
|
|
import v.pref
|
2021-01-14 15:20:11 +01:00
|
|
|
|
2021-01-16 19:03:07 +01:00
|
|
|
fn (mut g Gen) embed_file_is_prod_mode() bool {
|
|
|
|
if g.pref.is_prod || 'debug_embed_file_in_prod' in g.pref.compile_defines {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-01-14 15:20:11 +01:00
|
|
|
// gen_embed_file_struct generates C code for `$embed_file('...')` calls.
|
2021-12-04 18:43:19 +01:00
|
|
|
fn (mut g Gen) gen_embed_file_init(mut node ast.ComptimeCall) {
|
2022-01-25 09:58:23 +01:00
|
|
|
$if trace_embed_file ? {
|
|
|
|
eprintln('> gen_embed_file_init $node.embed_file.apath')
|
|
|
|
}
|
2021-12-04 18:43:19 +01:00
|
|
|
if g.embed_file_is_prod_mode() {
|
|
|
|
file_bytes := os.read_bytes(node.embed_file.apath) or {
|
|
|
|
panic('unable to read file: "$node.embed_file.rpath')
|
|
|
|
}
|
|
|
|
|
|
|
|
if node.embed_file.compression_type == 'none' {
|
|
|
|
node.embed_file.bytes = file_bytes
|
|
|
|
} else {
|
|
|
|
cache_dir := os.join_path(os.vmodules_dir(), 'cache', 'embed_file')
|
|
|
|
cache_key := rand.ulid()
|
|
|
|
// cache_key := md5.hexhash(node.embed_file.apath)
|
|
|
|
if !os.exists(cache_dir) {
|
|
|
|
os.mkdir_all(cache_dir) or { panic(err) }
|
|
|
|
}
|
|
|
|
cache_path := os.join_path(cache_dir, cache_key)
|
|
|
|
|
|
|
|
vexe := pref.vexe_path()
|
2022-01-25 09:58:23 +01:00
|
|
|
compress_cmd := '${os.quoted_path(vexe)} compress $node.embed_file.compression_type ${os.quoted_path(node.embed_file.apath)} ${os.quoted_path(cache_path)}'
|
|
|
|
$if trace_embed_file ? {
|
|
|
|
eprintln('> gen_embed_file_init, compress_cmd: $compress_cmd')
|
|
|
|
}
|
|
|
|
result := os.execute(compress_cmd)
|
2021-12-04 18:43:19 +01:00
|
|
|
if result.exit_code != 0 {
|
|
|
|
eprintln('unable to compress file "$node.embed_file.rpath": $result.output')
|
|
|
|
node.embed_file.bytes = file_bytes
|
|
|
|
} else {
|
|
|
|
compressed_bytes := os.read_bytes(cache_path) or {
|
|
|
|
eprintln('unable to read compressed file')
|
|
|
|
{
|
|
|
|
}
|
2022-04-15 14:30:37 +02:00
|
|
|
[]u8{}
|
2021-12-04 18:43:19 +01:00
|
|
|
}
|
|
|
|
os.rm(cache_path) or {} // clean up
|
|
|
|
node.embed_file.is_compressed = compressed_bytes.len > 0
|
|
|
|
&& compressed_bytes.len < file_bytes.len
|
|
|
|
node.embed_file.bytes = if node.embed_file.is_compressed {
|
|
|
|
compressed_bytes
|
|
|
|
} else {
|
|
|
|
file_bytes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if node.embed_file.bytes.len > 5242880 {
|
|
|
|
eprintln('embedding of files >= ~5MB is currently not well supported')
|
|
|
|
}
|
|
|
|
node.embed_file.len = file_bytes.len
|
|
|
|
}
|
2022-01-25 09:58:23 +01:00
|
|
|
ef_idx := node.embed_file.hash()
|
2022-01-25 11:50:19 +01:00
|
|
|
g.write('_v_embed_file_metadata( ${ef_idx}U )')
|
2022-01-25 09:58:23 +01:00
|
|
|
g.file.embedded_files << node.embed_file
|
|
|
|
$if trace_embed_file ? {
|
|
|
|
eprintln('> gen_embed_file_init => _v_embed_file_metadata(${ef_idx:-25}) | ${node.embed_file.apath:-50} | compression: $node.embed_file.compression_type | len: $node.embed_file.len')
|
2021-11-29 15:32:42 +01:00
|
|
|
}
|
2022-01-25 09:58:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// gen_embedded_metadata embeds all of the deduplicated metadata in g.embedded_files, into the V target executable,
|
|
|
|
// into a single generated function _v_embed_file_metadata, that accepts a hash of the absolute path of the embedded
|
|
|
|
// files.
|
|
|
|
fn (mut g Gen) gen_embedded_metadata() {
|
|
|
|
g.embedded_data.writeln('v__embed_file__EmbedFileData _v_embed_file_metadata(u64 ef_hash) {')
|
|
|
|
g.embedded_data.writeln('\tv__embed_file__EmbedFileData res;')
|
|
|
|
g.embedded_data.writeln('\tmemset(&res, 0, sizeof(res));')
|
|
|
|
g.embedded_data.writeln('\tswitch(ef_hash) {')
|
|
|
|
for emfile in g.embedded_files {
|
|
|
|
ef_idx := emfile.hash()
|
2022-01-25 11:50:19 +01:00
|
|
|
g.embedded_data.writeln('\t\tcase ${ef_idx}U: {')
|
2022-01-25 09:58:23 +01:00
|
|
|
g.embedded_data.writeln('\t\t\tres.path = ${ctoslit(emfile.rpath)};')
|
|
|
|
if g.embed_file_is_prod_mode() {
|
|
|
|
// apath is not needed in production and may leak information
|
|
|
|
g.embedded_data.writeln('\t\t\tres.apath = ${ctoslit('')};')
|
2021-12-04 18:43:19 +01:00
|
|
|
} else {
|
2022-01-25 09:58:23 +01:00
|
|
|
g.embedded_data.writeln('\t\t\tres.apath = ${ctoslit(emfile.apath)};')
|
2021-12-04 18:43:19 +01:00
|
|
|
}
|
2022-01-25 09:58:23 +01:00
|
|
|
if g.embed_file_is_prod_mode() {
|
|
|
|
// use function generated in Gen.gen_embedded_data()
|
|
|
|
if emfile.is_compressed {
|
|
|
|
g.embedded_data.writeln('\t\t\tres.compression_type = ${ctoslit(emfile.compression_type)};')
|
|
|
|
g.embedded_data.writeln('\t\t\tres.compressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, ${ctoslit(emfile.rpath)}, ${ctoslit(emfile.compression_type)})->data;')
|
|
|
|
g.embedded_data.writeln('\t\t\tres.uncompressed = NULL;')
|
|
|
|
} else {
|
|
|
|
g.embedded_data.writeln('\t\t\tres.uncompressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, ${ctoslit(emfile.rpath)}, ${ctoslit(emfile.compression_type)})->data;')
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
g.embedded_data.writeln('\t\t\tres.uncompressed = NULL;')
|
2021-12-04 18:43:19 +01:00
|
|
|
}
|
2022-01-25 09:58:23 +01:00
|
|
|
g.embedded_data.writeln('\t\t\tres.free_compressed = 0;')
|
|
|
|
g.embedded_data.writeln('\t\t\tres.free_uncompressed = 0;')
|
|
|
|
if g.embed_file_is_prod_mode() {
|
|
|
|
g.embedded_data.writeln('\t\t\tres.len = $emfile.len;')
|
|
|
|
} else {
|
|
|
|
file_size := os.file_size(emfile.apath)
|
|
|
|
if file_size > 5242880 {
|
|
|
|
eprintln('Warning: embedding of files >= ~5MB is currently not supported')
|
|
|
|
}
|
|
|
|
g.embedded_data.writeln('\t\t\tres.len = $file_size;')
|
|
|
|
}
|
|
|
|
g.embedded_data.writeln('\t\t\tbreak;')
|
|
|
|
g.embedded_data.writeln('\t\t} // case $ef_idx')
|
2021-12-04 18:43:19 +01:00
|
|
|
}
|
2022-01-25 09:58:23 +01:00
|
|
|
g.embedded_data.writeln('\t\tdefault: _v_panic(_SLIT("unknown embed file"));')
|
|
|
|
g.embedded_data.writeln('\t} // switch')
|
|
|
|
g.embedded_data.writeln('\treturn res;')
|
|
|
|
g.embedded_data.writeln('}')
|
2021-01-14 15:20:11 +01:00
|
|
|
}
|
|
|
|
|
2021-05-30 19:47:38 +02:00
|
|
|
// gen_embedded_data embeds data into the V target executable.
|
2021-01-14 15:20:11 +01:00
|
|
|
fn (mut g Gen) gen_embedded_data() {
|
|
|
|
/*
|
|
|
|
TODO implement support for large files - right now the setup has problems
|
|
|
|
// with even just 10 - 50 MB files - the problem is both in V and C compilers.
|
|
|
|
// maybe we need to write to separate files or have an external tool for large files
|
|
|
|
// like the `rcc` tool in Qt?
|
|
|
|
*/
|
|
|
|
for i, emfile in g.embedded_files {
|
2021-12-04 18:43:19 +01:00
|
|
|
g.embedded_data.write_string('static const unsigned char _v_embed_blob_$i[$emfile.bytes.len] = {\n ')
|
|
|
|
for j := 0; j < emfile.bytes.len; j++ {
|
|
|
|
b := emfile.bytes[j].hex()
|
|
|
|
if j < emfile.bytes.len - 1 {
|
2021-02-22 12:18:11 +01:00
|
|
|
g.embedded_data.write_string('0x$b,')
|
2021-01-14 15:20:11 +01:00
|
|
|
} else {
|
2021-02-22 12:18:11 +01:00
|
|
|
g.embedded_data.write_string('0x$b')
|
2021-01-14 15:20:11 +01:00
|
|
|
}
|
|
|
|
if 0 == ((j + 1) % 16) {
|
2021-02-22 12:18:11 +01:00
|
|
|
g.embedded_data.write_string('\n ')
|
2021-01-14 15:20:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
g.embedded_data.writeln('\n};')
|
|
|
|
}
|
|
|
|
g.embedded_data.writeln('')
|
2021-01-16 19:03:07 +01:00
|
|
|
g.embedded_data.writeln('const v__embed_file__EmbedFileIndexEntry _v_embed_file_index[] = {')
|
2021-01-14 15:20:11 +01:00
|
|
|
for i, emfile in g.embedded_files {
|
2021-12-04 18:43:19 +01:00
|
|
|
g.embedded_data.writeln('\t{$i, { .str=(byteptr)("${cestring(emfile.rpath)}"), .len=$emfile.rpath.len, .is_lit=1 }, { .str=(byteptr)("${cestring(emfile.compression_type)}"), .len=$emfile.compression_type.len, .is_lit=1 }, _v_embed_blob_$i},')
|
2021-01-14 15:20:11 +01:00
|
|
|
}
|
2021-12-04 18:43:19 +01:00
|
|
|
g.embedded_data.writeln('\t{-1, { .str=(byteptr)(""), .len=0, .is_lit=1 }, { .str=(byteptr)(""), .len=0, .is_lit=1 }, NULL}')
|
2021-01-14 15:20:11 +01:00
|
|
|
g.embedded_data.writeln('};')
|
2021-01-16 19:03:07 +01:00
|
|
|
// see vlib/v/embed_file/embed_file.v, find_index_entry_by_id/2 and find_index_entry_by_path/2
|
2021-01-14 15:20:11 +01:00
|
|
|
}
|