#include <stdlib.h>
#include "foundation.h"
-struct stream folder_string;
-struct stream tripple_string;
-b8 _setup_done = 0;
-
-const c8 *target_file_path = NULL;
-
-b8 use_tsan = 0;
-b8 use_asan = 0;
-b8 shared = 0;
-b8 no_pdb = 0;
-
-u32 optimise = 0;
+const c8 *_option_file_path = NULL;
+u64 _option_processors = 4;
+b8 _option_use_tsan = 0;
+b8 _option_use_asan = 0;
+b8 _option_no_pdb = 0;
+b8 _option_shared = 0;
+b8 _option_assemble_only = 0;
+u32 _option_optimise = 0;
+struct stream _assemble_file;
+struct keyvalues _assembly;
enum libc_version
{
k_libc_version_2_23,
k_libc_version_count,
}
-libc = k_libc_version_native;
+_option_libc = k_libc_version_native;
const c8 *libc_names[] =
{
[k_libc_version_native] = "",
k_architecture_x86_64,
k_architecture_count,
}
-arch = k_architecture_x86_64;
+_option_arch = k_architecture_x86_64;
const c8 *architecture_names[] =
{
[k_architecture_native] = "native",
k_platform_linux,
k_platform_count,
}
-platform = k_platform_linux;
+_option_platform = k_platform_linux;
const c8 *platform_names[] =
{
[k_platform_native] = "native",
[k_platform_windows] = "windows",
[k_platform_linux] = "linux",
};
-u64 processors = 4;
-
-void system_call( const c8 *call )
-{
- $log( $shell, {call} );
- ASSERT_CRITICAL( system(call) == 0 );
-}
-
struct
{
- c8 project_name[ 128 ];
- struct stream source_list, include_path_list, define_list, configuration;
+ struct stream name, tripple, folder;
const c8 *enabled_features[ 64 ];
}
static _metacompiler;
-struct
-{
- b8 using;
- struct stream layer_enums;
- struct stream input_enums, input_structures;
-}
-static _input;
-
-struct
-{
- b8 using;
- struct stream enums, structures;
-}
-static _threads;
-
-struct hook_event
-{
- struct stream alias, returns, proto;
- struct stretchy_allocator users;
-};
-struct hook_user
+struct shell_command
{
- struct stream function_name;
- i32 affinity;
+ u32 temp_frame;
+ struct stream line;
};
-struct
-{
- b8 using;
- struct stretchy_allocator events;
-}
-static _hooks;
-struct
+struct shell_command *_shell_command(void)
{
- b8 using;
- struct stream cvar_header, cvar_definitions, command_prototypes, command_definitions;
- u32 command_count;
+ u32 temp_frame = _start_temporary_frame();
+ struct shell_command *command = _temporary_allocate( sizeof(struct shell_command), 8 );
+ command->temp_frame = temp_frame;
+ stream_open_stack( &command->line, _temporary_stack_allocator(), k_stream_null_terminate );
+ return command;
}
-static _console;
-
-enum subshader_type
-{
- k_subshader_vertex,
- k_subshader_fragment,
- k_subshader_geometry
-};
-struct shader
-{
- struct stream name;
- struct subshader
- {
- enum subshader_type type;
- struct stream debug_source_list, static_source, uniform_source;
- u32 debug_source_count;
- }
- subshaders[3];
- u32 subshader_count;
- u32 uniform_start, uniform_count;
-};
-struct
+void _shell_run( struct shell_command *command )
{
- b8 using;
- struct stream shader_enum, uniform_enum, uniform_aliases, uniform_func_protos, uniform_funcs;
- u32 uniform_count;
- struct stretchy_allocator shaders;
+ $log( $shell, {string_get( &command->line )} );
+ ASSERT_CRITICAL( system( string_get( &command->line ) ) == 0 );
+ _end_temporary_frame( command->temp_frame );
}
-static _shaders;
-struct shell_command
-{
- struct stream command;
-};
-struct
+u32 _assembly_kv_file_context( u32 kv )
{
- struct stretchy_allocator shell;
-}
-static _commands;
+ u32 vgc_block = keyvalues_get( &_assembly, 0, "vgc", 0 );
+ ASSERT_CRITICAL( vgc_block );
-struct block_context
-{
- enum target_type
+ u32 narrowest = 0;
+ u32 it = 0;
+ while( keyvalues_foreach( &_assembly, &it, vgc_block, "file_context" ) )
{
- k_target_main,
- k_target_input_layer,
- k_target_input,
- k_target_shader,
- k_target_cvar,
- k_target_ccmd,
- k_target_thread,
- k_target_hook,
- k_target_subshader
+ u32 min = 0, max = 0;
+ keyvalues_read_u32s( &_assembly, it, "kv_start", NULL, &min, 1 );
+ keyvalues_read_u32s( &_assembly, it, "kv_end", NULL, &max, 1 );
+
+ if( (kv >= min) && (kv <= max) )
+ narrowest = it;
}
- target;
- u32 feature_count;
- const c8 *folder;
-};
+ return narrowest;
+}
-void _append_kv_list( const c8 *path, struct block_context context );
-void _parse_kv_block( struct keyvalues *kvs, u32 block, struct block_context context )
+void _assemble_kv_file( const c8 *path, u32 assembly_block, u32 feature_count );
+void _parse_kv_block( struct keyvalues *kvs, u32 block, u32 file_context, u32 assembly_block, u32 feature_count )
{
- const c8 *block_key = keyvalues_key( kvs, block, NULL );
- if( block_key != NULL )
+ u32 it = 0;
+ while( keyvalues_foreach( kvs, &it, block, NULL ) )
{
- if( context.target == k_target_shader )
+ const c8 *it_k = keyvalues_key( kvs, it, NULL );
+ if( keyvalues_type( kvs, it ) == k_keyvalue_type_frame )
{
- if( compare_buffers( block_key, 0, "subshader", 0 ))
+ if( compare_buffers( it_k, 0, "test_feature", 0 ) )
{
- context.target = k_target_subshader;
+ const c8 *test_feature = keyvalues_read_string( kvs, it, "feature", NULL );
+ ASSERT_CRITICAL( test_feature );
- struct shader *shader = stretchy_get( &_shaders.shaders, stretchy_count( &_shaders.shaders ) -1 );
- struct subshader *subshader = &shader->subshaders[ shader->subshader_count ++ ];
+ b8 result = 0;
+ for( u32 i=0; i<feature_count; i ++ )
+ if( compare_buffers( _metacompiler.enabled_features[i], 0, test_feature, 0 ) )
+ result = 1;
- const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
- ASSERT_CRITICAL( type );
-
- if( compare_buffers( type, 0, "vertex", 0 ) )
- subshader->type = k_subshader_vertex;
- else if( compare_buffers( type, 0, "fragment", 0 ) )
- subshader->type = k_subshader_fragment;
- else if( compare_buffers( type, 0, "geometry", 0 ) )
- subshader->type = k_subshader_geometry;
- else
- {
- $log( $warning, {"Invalid subshader type '"}, {type}, {"'"} );
- return;
- }
-
- subshader->debug_source_count = 0;
- stream_open_auto( &subshader->debug_source_list, k_stream_null_terminate );
- stream_open_auto( &subshader->static_source, k_stream_null_terminate );
- stream_open_auto( &subshader->uniform_source, k_stream_null_terminate );
- }
- else
- {
- $log( $warning, {"We don't have a compiler:shader definition for block '"}, {block_key}, {"'"} );
- return;
- }
- }
- else if( context.target == k_target_subshader )
- {
- struct shader *shader = stretchy_get( &_shaders.shaders, stretchy_count( &_shaders.shaders ) -1 );
- struct subshader *subshader = &shader->subshaders[ shader->subshader_count -1 ];
-
- if( compare_buffers( block_key, 0, "uniform", 0 ))
- {
- const c8 *alias = keyvalues_read_string( kvs, block, "alias", NULL );
- const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
- ASSERT_CRITICAL( type && alias );
-
- struct glsl_trans
- {
- const c8 *type, *args, *call, *end;
- }
- type_list[] =
- {
- { "int", "i32 b", "glUniform1i", "b" },
- { "vec2", "f32 v[2]", "glUniform2fv", "1,v" },
- { "vec3", "f32 v[3]", "glUniform3fv", "1,v" },
- { "vec4", "f32 v[4]", "glUniform4fv", "1,v" },
- { "bool", "i32 b", "glUniform1i", "b" },
- { "mat2", "f32 m[2][2]", "glUniformMatrix2fv", "1,GL_FALSE,(f32*)m" },
- { "mat3", "f32 m[3][3]", "glUniformMatrix3fv", "1,GL_FALSE,(f32*)m" },
- { "mat4", "f32 m[4][4]", "glUniformMatrix4fv", "1,GL_FALSE,(f32*)m" },
- { "float", "f32 f", "glUniform1f", "f" },
- { "mat4x3", "f32 m[4][3]", "glUniformMatrix4x3fv", "1,GL_FALSE,(f32*)m" },
- { "sampler2D", "i32 i", "glUniform1i", "i" },
- { "usampler3D", "i32 i", "glUniform1i", "i" },
- { "samplerCube", "i32 i", "glUniform1i", "i" },
- { "samplerBuffer","i32 i", "glUniform1i", "i" },
- },
- *trans = NULL;
-
- for( u32 i=0; i<ARRAY_COUNT( type_list ); i ++ )
- {
- if( compare_buffers( type_list[i].type, 0, type, 0 ) )
- {
- trans = &type_list[i];
- break;
- }
- }
- ASSERT_CRITICAL( trans );
-
- $v_string( &_shaders.uniform_enum, {" k_shader_"}, {string_get(&shader->name)}, {"_"}, {alias}, {",\n"} );
- $v_string( &_shaders.uniform_aliases, {" [k_shader_"}, {string_get(&shader->name)}, {"_"}, {alias}, {"] = \""},
- {alias},{"\",\n"} );
- $v_string( &subshader->uniform_source, {"uniform "}, {type}, {" "}, {alias}, {";\\n"} );
-
- $v_string( &_shaders.uniform_func_protos, {"void _shader_"}, {string_get(&shader->name)}, {"_"}, {alias}, {"( "},{trans->args},{" );\n"} );
- $v_string( &_shaders.uniform_funcs, {"void _shader_"}, {string_get(&shader->name)}, {"_"}, {alias}, {"( "},{trans->args},{" )"},
- {"{ "},{trans->call},{"( _uniform_locations["},
- {"k_shader_"}, {string_get(&shader->name)}, {"_"}, {alias},
- {"], "},{trans->end},{" ); }\n"});
-
- shader->uniform_count ++;
- _shaders.uniform_count ++;
+ u32 pass_block = keyvalues_get( kvs, it, result? "yes": "no", 0 );
+ if( pass_block )
+ _parse_kv_block( kvs, pass_block, file_context, assembly_block, feature_count );
}
else
{
- $log( $warning, {"We don't have a compiler:subshader definition for block '"}, {block_key}, {"'"} );
- return;
+ /* Add the block title to the assembly if it's not a pre-processor thing */
+ _parse_kv_block( kvs, it, file_context, keyvalues_append_frame( &_assembly, assembly_block, it_k ), feature_count );
}
}
else
{
- ASSERT_CRITICAL( context.target == k_target_main );
- if( compare_buffers( block_key, 0, "input_layer", 0 ))
- {
- _input.using = 1;
- context.target = k_target_input_layer;
- const c8 *layer_name = keyvalues_read_string( kvs, block, "name", NULL );
- ASSERT_CRITICAL( layer_name );
-
- $v_string( &_input.layer_enums, {" k_input_layer_"}, {layer_name}, {",\n"} );
- }
- else if( compare_buffers( block_key, 0, "input", 0 ))
- {
- _input.using = 1;
- context.target = k_target_input;
-
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- ASSERT_CRITICAL( name );
-
- const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
- ASSERT_CRITICAL( type );
-
- const c8 *layer_mask = keyvalues_read_string( kvs, block, "layer_mask", NULL );
-
- $v_string( &_input.input_enums, {" k_input_"},{type},{"_"}, {name}, {",\n"} );
- $v_string( &_input.input_structures, {" [k_input_"},{type},{"_"},{name},{"]=\n {\n"} );
- $v_string( &_input.input_structures, {" .name = \""},{name},{"\",\n"} );
- $v_string( &_input.input_structures, {" .type = k_input_type_"},{type},{",\n"} );
-
- if( layer_mask )
- $v_string( &_input.input_structures, {" .layer_mask = 1<<k_input_layer_"}, {layer_mask}, {",\n"} );
-#if 0
- const c8 *keyboard_key = keyvalues_read_string( kvs, block, "keyboard", NULL );
- if( keyboard_key )
- $v_string( &_input.button_structures, {" .key_id = GLFW_KEY_"}, {keyboard_key}, {",\n"} );
-#endif
- $v_string( &_input.input_structures, {" },\n"} );
- }
- else if( compare_buffers( block_key, 0, "cvar", 0 ))
- {
- _console.using = 1;
- context.target = k_target_cvar;
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
- const c8 *default_value = keyvalues_read_string( kvs, block, "default", NULL );
- ASSERT_CRITICAL( name && type && default_value );
-
- $v_string( &_console.cvar_header, {"extern "}, {type}, {" cvar_"}, {name}, {";\n"} );
- $v_string( &_console.cvar_definitions, {type}, {" cvar_"}, {name}, {" = "}, {default_value}, {";\n"} );
- }
- else if( compare_buffers( block_key, 0, "ccmd", 0 ))
- {
- _console.using = 1;
- _console.command_count ++;
- context.target = k_target_ccmd;
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- const c8 *function = keyvalues_read_string( kvs, block, "function", NULL );
- ASSERT_CRITICAL( name && function );
- $v_string( &_console.command_definitions, {" {\n .alias = \""}, {name}, {"\",\n .fn = "},
- {function}, {"\n },\n"} );
- $v_string( &_console.command_prototypes, {"i32 "}, {function}, {"( struct console_arguments *args );\n"} );
- // TODO: process parameter descriptions
- return;
- }
- else if( compare_buffers( block_key, 0, "shader", 0 ))
- {
- _shaders.using = 1;
- context.target = k_target_shader;
- struct shader *shader = stretchy_append( &_shaders.shaders );
- shader->subshader_count = 0;
- shader->uniform_start = _shaders.uniform_count;
- shader->uniform_count = 0;
-
- stream_open_auto( &shader->name, k_stream_null_terminate );
- $v_string( &shader->name, {keyvalues_read_string( kvs, block, "name", NULL )} );
- $v_string( &_shaders.shader_enum, {" k_shader_"}, {string_get(&shader->name)}, {",\n"} );
- }
- else if( compare_buffers( block_key, 0, "thread", 0 ))
- {
- _threads.using = 1;
- context.target = k_target_thread;
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- ASSERT_CRITICAL( name );
-
- $v_string( &_threads.enums, {" k_thread_"},{name}, {",\n"} );
- $v_string( &_threads.structures, {" [k_thread_"},{name},{"]=\n {\n"} );
- $v_string( &_threads.structures, {" .name = \""}, {name}, {"\",\n"} );
-
- u32 queue_size_m = 0;
- if( keyvalues_read_u32s( kvs, block, "queue_size_m", NULL, &queue_size_m, 1 ) )
- $v_string( &_threads.structures, {" .queue_size_m = "}, $unsigned(queue_size_m), {",\n"} );
-
-
- u32 flag_kv = keyvalues_get( kvs, block, "flag", 0 );
- if( flag_kv )
- $v_string( &_threads.structures, {" .flags = "} );
-
- u32 flag_count = 0;
- while( flag_kv )
- {
- const c8 *flag_name = keyvalues_value( kvs, flag_kv, NULL );
- $v_string( &_threads.structures, {flag_count? "|": ""}, {"THREAD_FLAG_"}, {flag_name} );
-
- flag_kv = keyvalues_get( kvs, flag_kv, "flag", 1 );
- flag_count ++;
- }
- if( flag_count )
- $v_string( &_threads.structures, {",\n"} );
-
- $v_string( &_threads.structures, {" },\n"} );
- }
- else if( compare_buffers( block_key, 0, "event", 0 ))
- {
- _hooks.using = 1;
- context.target = k_target_hook;
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- const c8 *prototype = keyvalues_read_string( kvs, block, "prototype", NULL );
- const c8 *returns = keyvalues_read_string( kvs, block, "returns", "void" );
- ASSERT_CRITICAL( name && prototype && returns );
-
- struct hook_event *event = stretchy_append( &_hooks.events );
-
- stream_open_auto( &event->alias, k_stream_null_terminate );
- $v_string( &event->alias, {name} );
- stream_open_auto( &event->returns, k_stream_null_terminate );
- $v_string( &event->returns, {returns} );
- stream_open_auto( &event->proto, k_stream_null_terminate );
- $v_string( &event->proto, {prototype} );
-
- stretchy_init( &event->users, sizeof( struct hook_user ) );
- }
- else if( compare_buffers( block_key, 0, "hook", 0 ))
- {
- _hooks.using = 1;
- const c8 *event_alias = keyvalues_read_string( kvs, block, "event", NULL );
- const c8 *function = keyvalues_read_string( kvs, block, "function", NULL );
-
- ASSERT_CRITICAL( event_alias && function );
-
- i32 affinity = 0;
- keyvalues_read_i32s( kvs, block, "affinity", NULL, &affinity, 1 );
-
- b8 found = 0;
-
- for( u32 i=0; i<stretchy_count( &_hooks.events ); i ++ )
- {
- struct hook_event *event = stretchy_get( &_hooks.events, i );
- if( compare_buffers( string_get( &event->alias ), 0, event_alias, 0 ) )
- {
- struct hook_user *user = stretchy_append( &event->users );
- stream_open_auto( &user->function_name, k_stream_null_terminate );
- $v_string( &user->function_name, {function} );
- user->affinity = affinity;
- found = 1;
- break;
- }
- }
-
- if( !found )
- {
- $log( $fatal, {"We don't have an event registered called '"}, {event_alias}, {"'"} );
- _fatal_exit();
- }
- }
- else if( compare_buffers( block_key, 0, "symlink", 0 ) )
- {
- const c8 *source = keyvalues_read_string( kvs, block, "source", NULL );
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- ASSERT_CRITICAL( source );
-
- struct shell_command *command = stretchy_append( &_commands.shell );
- stream_open_auto( &command->command, k_stream_null_terminate );
- $v_string( &command->command, {"ln -snf "}, {context.folder}, {"/"}, {source}, {" "}, {string_get( &folder_string )}, {"/"}, { name? name: source } );
- }
- else
- {
- $log( $warning, {"We don't have a compiler definition for block '"}, {block_key}, {"'"} );
- return;
- }
- }
- }
-
- u32 kv = keyvalues_get_child( kvs, block, 0 );
- while( kv )
- {
- if( keyvalues_type( kvs, kv ) == k_keyvalue_type_frame )
- {
- _parse_kv_block( kvs, kv, context );
- kv = keyvalues_get_next( kvs, kv );
- continue;
- }
-
- const c8 *key = keyvalues_key( kvs, kv, NULL );
- const c8 *value = keyvalues_value( kvs, kv, NULL );
-
- /* MUST COME FIRST */
- if( _setup_done == 0 )
- {
- if( compare_buffers( key, 0, "name", 0 ) )
- {
- buffer_copy( value, 0, _metacompiler.project_name, sizeof(_metacompiler.project_name) );
-
- c8 *output_folder = realpath( target_file_path, NULL );
- if( !output_folder )
- {
- $log( $fatal, {"'"}, {target_file_path}, {"' "}, $errno() );
- _fatal_exit();
- }
-
- i32 s = buffer_last_index( output_folder, '/', 0 );
- if( s != -1 )
- output_folder[s] = '\0';
-
-
- stream_open_auto( &tripple_string, k_stream_null_terminate );
- $v_string( &tripple_string, {architecture_names[arch]}, {"-"}, {platform_names[platform]} );
- if( platform == k_platform_linux )
- {
- $v_string( &tripple_string, {"-gnu"} );
- if( libc != k_libc_version_native )
- $v_string( &tripple_string, {"."}, {libc_names[libc]} );
- }
-
- stream_open_auto( &folder_string, k_stream_null_terminate );
- $v_string( &folder_string, {output_folder}, {"/bin/"}, {_metacompiler.project_name}, {"-"}, {string_get(&tripple_string)} );
-
- free(output_folder);
-
- u32 temp_frame = _start_temporary_frame();
- {
- struct stream command_string;
- stream_open_stack( &command_string, _temporary_stack_allocator(), k_stream_null_terminate );
- $v_string( &command_string, {"mkdir -p "}, {string_get( &folder_string )} );
- system_call( string_get( &command_string ) );
- }
- _end_temporary_frame( temp_frame );
-
- _setup_done = 1;
- }
- }
-
- ASSERT_CRITICAL( _setup_done );
-
- /* availible in all targets */
- if( compare_buffers( key, 0, "enable", 0 ) )
- _metacompiler.enabled_features[ context.feature_count ++ ] = keyvalues_value( kvs, kv, NULL );
-
- if( compare_buffers( key, 0, "if", 0 ) )
- {
- b8 invert = 0;
- if( value[0] == '!' )
- {
- value ++;
- invert = 1;
- }
-
- b8 result = 0 ^ invert;
- for( u32 i=0; i<context.feature_count; i ++ )
- if( compare_buffers( _metacompiler.enabled_features[i], 0, value, 0 ) )
- result = 1 ^ invert;
-
- if( !result )
- return;
- }
-
- if( compare_buffers( key, 0, "append", 0 ) )
- {
- u32 temp_frame = _start_temporary_frame();
- {
- struct stream path_string;
- stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate );
- $v_string( &path_string, {context.folder}, {"/"}, {value} );
- _append_kv_list( string_get( &path_string ), context );
- }
- _end_temporary_frame( temp_frame );
- }
-
- if( compare_buffers( key, 0, "config", 0 ) )
- $v_string( &_metacompiler.configuration, {value}, {"\n"} );
-
- if( context.target == k_target_main )
- {
- if( compare_buffers( key, 0, "object_type", 0 ) )
- {
- if( compare_buffers( value, 0, "executable", 0 ) ) shared = 0;
- else if( compare_buffers( value, 0, "shared", 0 ) ) shared = 1;
- else
- {
- $log( $fatal, {"Unknown output type '"}, {value}, {"'"} );
- _fatal_exit();
- }
- }
-
- if( compare_buffers( key, 0, "add", 0 ) )
- $v_string( &_metacompiler.source_list, {" "}, {context.folder}, {"/"}, {value}, {" \\\n"} );
-
- if( compare_buffers( key, 0, "include", 0 ) )
- $v_string( &_metacompiler.include_path_list, {" -I"}, {context.folder}, {"/"}, {value}, {" \\\n"} );
-
- if( compare_buffers( key, 0, "define", 0 ) )
- $v_string( &_metacompiler.include_path_list, {" -D"}, {value}, {" \\\n"} );
+ const c8 *it_v = keyvalues_value( kvs, it, NULL );
- if( compare_buffers( key, 0, "link", 0 ) )
- $v_string( &_metacompiler.include_path_list, {" -l"}, {value}, {" \\\n"} );
- if( compare_buffers( key, 0, "link_folder", 0 ) )
- $v_string( &_metacompiler.include_path_list, {" -L"}, {value}, {" \\\n"} );
- }
- else if( context.target == k_target_subshader )
- {
- struct shader *shader = stretchy_get( &_shaders.shaders, stretchy_count( &_shaders.shaders ) -1 );
- struct subshader *subshader = &shader->subshaders[ shader->subshader_count -1 ];
+ /* ENABLE FEATURE */
+ if( compare_buffers( it_k, 0, "enable", 0 ) )
+ _metacompiler.enabled_features[ feature_count ++ ] = it_v;
- if( compare_buffers( key, 0, "add", 0 ) )
+ /* APPEND */
+ else if( compare_buffers( it_k, 0, "append", 0 ) )
{
u32 temp_frame = _start_temporary_frame();
{
struct stream path_string;
stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate );
- $v_string( &path_string, {context.folder}, {"/"}, {value} );
-
- struct stream source;
- ASSERT_CRITICAL( stream_open_file( &source, string_get( &path_string ), k_stream_read ) );
-
- u32 block_size = BYTES_KB(128);
- c8 *temp = _temporary_allocate( block_size, 4 );
- while(1)
- {
- u32 l = stream_read( &source, temp, block_size );
- $v_string( &subshader->static_source, {temp, .length = l} );
- if( l != block_size )
- break;
- }
+
+ const c8 *folder = keyvalues_read_string( &_assembly, file_context, "folder", NULL );
+ ASSERT_CRITICAL( folder );
- stream_close( &source );
- $v_string( &subshader->debug_source_list, {" \""}, {string_get(&path_string)}, {"\",\n"} );
- subshader->debug_source_count ++;
+ $v_string( &path_string, {folder}, {"/"}, {it_v} );
+ _assemble_kv_file( string_get( &path_string ), assembly_block, feature_count );
}
_end_temporary_frame( temp_frame );
}
- }
- kv = keyvalues_get_next( kvs, kv );
+ /* NORMAL KV */
+ else
+ keyvalues_append_string( &_assembly, assembly_block, it_k, it_v );
+ }
}
}
-void _append_kv_list( const c8 *path, struct block_context context )
+void _assemble_kv_file( const c8 *path, u32 assembly_block, u32 feature_count )
{
- u32 temp_frame = _start_temporary_frame();
+ /* Append context block */
+ u32 vgc_block = keyvalues_get( &_assembly, 0, "vgc", 0 );
+ ASSERT_CRITICAL( vgc_block );
+ u32 context_block = keyvalues_append_frame( &_assembly, vgc_block, "file_context" );
+ u32 start_offset = keyvalues_current_offset( &_assembly );
+
+ u32 temp_frame = _start_temporary_frame();
c8 *folder = realpath( path, NULL );
if( !folder )
{
i32 s = buffer_last_index( folder, '/', 0 );
if( s != -1 )
- {
folder[s] = '\0';
- }
- context.folder = folder;
+ keyvalues_append_string( &_assembly, context_block, "folder", folder );
+ free( folder );
- struct keyvalues list;
- ASSERT_CRITICAL( keyvalues_read_file( &list, path, _temporary_stack_allocator() ) );
- _parse_kv_block( &list, 0, context );
+ /* read file and pre-process it into the assembly */
+ struct keyvalues kvs;
+ ASSERT_CRITICAL( keyvalues_read_file( &kvs, path, _temporary_stack_allocator() ) );
+ _parse_kv_block( &kvs, 0, context_block, assembly_block, feature_count );
- free( folder );
+ u32 end_offset = keyvalues_current_offset( &_assembly );
+ keyvalues_append_u32s( &_assembly, context_block, "kv_start", &start_offset, 1 );
+ keyvalues_append_u32s( &_assembly, context_block, "kv_end", &end_offset, 1 );
_end_temporary_frame( temp_frame );
}
{
const c8 *arg;
if( _option_long( "tsan", "Build using thread sanitizer" ) )
- use_tsan = 1;
+ _option_use_tsan = 1;
if( _option_long( "asan", "Build using address sanitizer" ) )
- use_asan = 1;
+ _option_use_asan = 1;
- if( use_tsan || use_asan )
- ASSERT_CRITICAL( use_tsan != use_asan );
+ if( _option_use_tsan || _option_use_asan )
+ ASSERT_CRITICAL( _option_use_tsan != _option_use_asan );
if( _option_long( "shared", "Create a shared object, instead of executable" ) )
- shared = 1;
+ _option_shared = 1;
if( _option_long( "no-pdb", "Prevent compiler from outputing pdb files" ) )
- no_pdb = 1;
+ _option_no_pdb = 1;
if( (arg = _option_long_argument( "libc", "native, 2.23 (recommended)" )) )
- libc = enum_read( libc_names, k_libc_version_count, arg );
+ _option_libc = enum_read( libc_names, k_libc_version_count, arg );
if( (arg = _option_long_argument( "arch", "native, i386, x86_64" )) )
- arch = enum_read( architecture_names, k_architecture_count, arg );
- ASSERT_CRITICAL( arch == k_architecture_x86_64 );
+ _option_arch = enum_read( architecture_names, k_architecture_count, arg );
+ ASSERT_CRITICAL( _option_arch == k_architecture_x86_64 );
if( (arg = _option_long_argument( "platform", "native, linux, windows" )) )
- platform = enum_read( platform_names, k_platform_count, arg );
+ _option_platform = enum_read( platform_names, k_platform_count, arg );
if( (arg = _option_argument( 'j', "CPU count <0-8>" )) )
{
struct stream arg_stream;
stream_open_buffer_read( &arg_stream, (void*)arg, buffer_last_index(arg,0,0)+1, 0 );
- ASSERT_CRITICAL( string_parse_u64( &arg_stream, &processors ) == k_string_parse_ok );
+ ASSERT_CRITICAL( string_parse_u64( &arg_stream, &_option_processors ) == k_string_parse_ok );
}
if( _option_flag( 'O', "Run optimisers" ) )
- optimise = 3;
+ _option_optimise = 3;
+
+ if( (arg = _option_argument( 'E', "Assemble target KV's only, and output it to file" )) )
+ {
+ _option_assemble_only = 1;
+ ASSERT_CRITICAL( stream_open_file( &_assemble_file, arg, k_stream_write ) );
+ }
- target_file_path = _option();
- ASSERT_CRITICAL( target_file_path );
+ _option_file_path = _option();
+ ASSERT_CRITICAL( _option_file_path );
}
void (*_event_OPTIONS_subscribers[])( void ) =
NULL
};
-i32 main( i32 argc, const c8 *argv[] )
+i32 compar( const void *a, const void *b )
{
- VG_PRE_MAIN;
+ return ((struct sort_index *)a)->value - ((struct sort_index *)b)->value;
+}
- u32 options = k_stream_null_terminate;
- stream_open_auto( &_metacompiler.source_list, options );
- stream_open_auto( &_metacompiler.include_path_list, options );
- stream_open_auto( &_metacompiler.define_list, options );
- stream_open_auto( &_metacompiler.configuration, options );
- stream_open_auto( &_input.layer_enums, options );
- stream_open_auto( &_input.input_enums, options );
- stream_open_auto( &_input.input_structures, options );
- stream_open_auto( &_console.cvar_header, options );
- stream_open_auto( &_console.cvar_definitions, options );
- stream_open_auto( &_console.command_definitions, options );
- stream_open_auto( &_console.command_prototypes, options );
- stream_open_auto( &_threads.enums, options );
- stream_open_auto( &_threads.structures, options );
- stream_open_auto( &_shaders.shader_enum, options );
- stream_open_auto( &_shaders.uniform_enum, options );
- stream_open_auto( &_shaders.uniform_aliases, options );
- stream_open_auto( &_shaders.uniform_funcs, options );
- stream_open_auto( &_shaders.uniform_func_protos, options );
- stretchy_init( &_hooks.events, sizeof( struct hook_event ) );
- stretchy_init( &_shaders.shaders, sizeof( struct shader ) );
- stretchy_init( &_commands.shell, sizeof( struct shell_command ) );
-
- if( platform == k_platform_linux ) _metacompiler.enabled_features[0] = "linux";
- if( platform == k_platform_windows ) _metacompiler.enabled_features[0] = "windows";
-
- struct block_context context = {0};
- context.feature_count = 1;
- _append_kv_list( target_file_path, context );
-
- ASSERT_CRITICAL( _setup_done );
-
- system_call( "mkdir -p generated" );
-
- $log( $info, {"Generating input header"} );
- if( _input.using )
- {
- struct stream input_header, input_source;
- ASSERT_CRITICAL( stream_open_file( &input_header, "generated/input.h", k_stream_write ) );
- $v_string( &input_header, {"enum input_layer_id\n{\n"}, {string_get( &_input.layer_enums )}, {" k_input_layer_count\n};\n"} );
- $v_string( &input_header, {"enum input_id\n{\n"}, {string_get( &_input.input_enums )}, {" k_input_count\n};\n"} );
- stream_close( &input_header );
-
- ASSERT_CRITICAL( stream_open_file( &input_source, "generated/input.c", k_stream_write ) );
- $v_string( &input_source, {"struct input_info _input_infos[k_input_count] = \n{\n"},
- {string_get( &_input.input_structures )}, {"};\n"} );
- stream_close( &input_source );
- }
+void index_sort( struct sort_index *indices, u32 indice_count )
+{
+ qsort( indices, indice_count, sizeof(struct sort_index), compar );
+}
- $log( $info, {"Generating threads header"} );
- if( _threads.using )
+void _codegen_hooks(void)
+{
+ $log( $info, {"[CODEGEN] hooks.h/hooks.c"} );
+ struct stream hooks_c, hooks_h;
+ stream_open_file( &hooks_h, "generated/hooks.h", k_stream_write );
+ stream_open_file( &hooks_c, "generated/hooks.c", k_stream_write );
+
+ u32 it = 0;
+ while( keyvalues_foreach( &_assembly, &it, 0, "event" ) )
{
- struct stream thread_header, thread_source;
- ASSERT_CRITICAL( stream_open_file( &thread_header, "generated/threads.h", k_stream_write ) );
- $v_string( &thread_header, {"enum thread_id\n{\n"},
- {string_get( &_threads.enums )},
- {" k_thread_count\n};\n"} );
- stream_close( &thread_header );
-
- ASSERT_CRITICAL( stream_open_file( &thread_source, "generated/threads.c", k_stream_write ) );
- $v_string( &thread_source, {"struct thread_info _thread_infos[k_thread_count] = \n{\n"},
- {string_get( &_threads.structures )}, {"};\n"} );
- stream_close( &thread_source );
+ const c8 *name = keyvalues_read_string( &_assembly, it, "name", NULL );
+ const c8 *prototype = keyvalues_read_string( &_assembly, it, "prototype", NULL );
+ const c8 *returns = keyvalues_read_string( &_assembly, it, "returns", "void" );
+ ASSERT_CRITICAL( name && prototype && returns );
+
+ $v_string( &hooks_h, {"extern "}, {returns}, {" (*_event_"}, {name}, {"_subscribers[])( "}, {prototype}, {" );\n"} );
+
+ struct sort_index indices[ 128 ];
+ u32 indice_count = 0;
+
+ u32 user_it = 0;
+ while( keyvalues_foreach( &_assembly, &user_it, 0, "hook" ) )
+ {
+ if( compare_buffers( keyvalues_read_string( &_assembly, user_it, "event", NULL ), 0, name, 0 ) )
+ {
+ i32 affinity = 0;
+ keyvalues_read_i32s( &_assembly, user_it, "affinity", NULL, &affinity, 1 );
+
+ ASSERT_CRITICAL( indice_count < ARRAY_COUNT( indices ) );
+ indices[ indice_count ].index = user_it;
+ indices[ indice_count ++ ].value = affinity;
+
+ const c8 *function = keyvalues_read_string( &_assembly, user_it, "function", NULL );
+ ASSERT_CRITICAL( function );
+ $v_string( &hooks_c, {returns}, {" "}, {function}, {"( "}, {prototype}, {" );\n"} );
+ }
+ }
+
+ index_sort( indices, indice_count );
+
+ $v_string( &hooks_c, {returns}, {" (*_event_"}, {name}, {"_subscribers[])( "}, {prototype}, {" ) = \n{\n"} );
+ for( u32 i=0; i<indice_count; i ++ )
+ {
+ const c8 *function = keyvalues_read_string( &_assembly, indices[i].index, "function", NULL );
+ ASSERT_CRITICAL( function );
+ $v_string( &hooks_c, {" "}, {function}, {",\n"} );
+ }
+ $v_string( &hooks_c, {" NULL\n};\n\n"} );
}
- $log( $info, {"Generating console header"} );
- if( _console.using )
+ stream_close( &hooks_h );
+ stream_close( &hooks_c );
+}
+
+void _codegen_shaders(void)
+{
+ $log( $info, {"[CODEGEN] shaders.h/shaders.c"} );
+ struct stream C, H;
+ stream_open_file( &H, "generated/shaders.h", k_stream_write );
+ stream_open_file( &C, "generated/shaders.c", k_stream_write );
+
+ struct uniform_info
{
- struct stream console_header, console_source;
- ASSERT_CRITICAL( stream_open_file( &console_header, "generated/console.h", k_stream_write ) );
- $v_string( &console_header, {string_get( &_console.cvar_header )} );
- stream_close( &console_header );
-
- ASSERT_CRITICAL( stream_open_file( &console_source, "generated/console.c", k_stream_write ) );
- $v_string( &console_source, {string_get( &_console.cvar_definitions )} );
- $v_string( &console_source, {string_get( &_console.command_prototypes )} );
- $v_string( &console_source, {"static struct console_command _console_commands[] = \n{\n"},
- {string_get( &_console.command_definitions )}, {"};\n\n"} );
- stream_close( &console_source );
+ u32 kv;
+ i32 glsl_index;
+ const c8 *type;
+ const c8 *alias;
+ const c8 *shader_name;
+ };
+ struct stretchy_allocator uniforms;
+ stretchy_init( &uniforms, sizeof(struct uniform_info) );
+
+ struct glsl_trans
+ {
+ const c8 *type, *args, *call, *end;
}
-
- $log( $info, {"Generating hooks header"} );
- if( _hooks.using )
+ glsl_list[] =
{
- struct stream header, source;
- ASSERT_CRITICAL( stream_open_file( &header, "generated/hooks.h", k_stream_write ) );
- ASSERT_CRITICAL( stream_open_file( &source, "generated/hooks.c", k_stream_write ) );
-
- for( u32 i=0; i<stretchy_count( &_hooks.events ); i ++ )
- {
- struct hook_event *event = stretchy_get( &_hooks.events, i );
+ { "int", "i32 b", "glUniform1i", "b" },
+ { "vec2", "f32 v[2]", "glUniform2fv", "1,v" },
+ { "vec3", "f32 v[3]", "glUniform3fv", "1,v" },
+ { "vec4", "f32 v[4]", "glUniform4fv", "1,v" },
+ { "bool", "i32 b", "glUniform1i", "b" },
+ { "mat2", "f32 m[2][2]", "glUniformMatrix2fv", "1,GL_FALSE,(f32*)m" },
+ { "mat3", "f32 m[3][3]", "glUniformMatrix3fv", "1,GL_FALSE,(f32*)m" },
+ { "mat4", "f32 m[4][4]", "glUniformMatrix4fv", "1,GL_FALSE,(f32*)m" },
+ { "float", "f32 f", "glUniform1f", "f" },
+ { "mat4x3", "f32 m[4][3]", "glUniformMatrix4x3fv", "1,GL_FALSE,(f32*)m" },
+ { "sampler2D", "i32 i", "glUniform1i", "i" },
+ { "usampler3D", "i32 i", "glUniform1i", "i" },
+ { "samplerCube", "i32 i", "glUniform1i", "i" },
+ { "samplerBuffer","i32 i", "glUniform1i", "i" },
+ };
+
+ $v_string( &C, {"struct shader _shader_definitions[] = {\n"} );
+ $v_string( &H, {"enum shader_id\n{\n"} );
+ u32 it = 0;
+ while( keyvalues_foreach( &_assembly, &it, 0, "shader" ) )
+ {
+ u32 uniform_start = stretchy_count( &uniforms ),
+ uniform_count = 0;
- const c8 *returns = string_get( &event->returns ),
- *alias = string_get( &event->alias ),
- *prototype = string_get( &event->proto );
+ const c8 *shader_name = keyvalues_read_string( &_assembly, it, "name", NULL );
+ ASSERT_CRITICAL( shader_name );
+ $v_string( &H, {" k_shader_"},{shader_name},{",\n"} );
+ $v_string( &C, {" [k_shader_"},{shader_name},{"] = \n {\n"} );
+ $v_string( &C, {" .name = \""}, {shader_name}, {"\",\n"} );
+ $v_string( &C, {" .uniform_start = "}, $unsigned( uniform_start ), {",\n"} );
- $v_string( &header, {"extern "}, {returns}, {" (*_event_"}, {alias}, {"_subscribers[])( "}, {prototype}, {" );\n"} );
+ $v_string( &C, {" .subshaders = (struct subshader[])\n {\n"} );
- for( u32 j=0; j<stretchy_count( &event->users ); j ++ )
+ u32 subshader_count = 0;
+ u32 subshader_it = 0;
+ while( keyvalues_foreach( &_assembly, &subshader_it, it, "subshader" ) )
+ {
+ const c8 *subshader_type = keyvalues_read_string( &_assembly, subshader_it, "type", NULL );
+ ASSERT_CRITICAL( subshader_type );
+
+ $v_string( &C, {" {\n"} );
+ $v_string( &C, {" .type = k_subshader_"}, {subshader_type}, {",\n"} );
+ $v_string( &C, {" .source_list = (const c8 *[])\n"} );
+ $v_string( &C, {" {\n"} );
+
+ u32 sources[ 32 ];
+ u32 source_count = 0;
+ u32 source_it = 0;
+ while( keyvalues_foreach( &_assembly, &source_it, subshader_it, "add" ) )
{
- struct hook_user *user = stretchy_get( &event->users, j );
- $v_string( &source, {returns}, {" "}, {string_get(&user->function_name)}, {"( "}, {prototype}, {" );\n"} );
+ ASSERT_CRITICAL( source_count < ARRAY_COUNT( sources ) );
+
+ /* FIXME: EXPAND PATH TO FULL PATH LIKE SOURCE ADDS */
+ const c8 *path = keyvalues_value( &_assembly, source_it, NULL );
+ $v_string( &C, {" \""}, {path}, {"\",\n"} );
+ sources[ source_count ++ ] = source_it;
}
- $v_string( &source, {"\n"} );
- b8 sorted = 0;
- while( !sorted )
+ $v_string( &C, {" },\n"} );
+ $v_string( &C, {" .source_count = "}, $unsigned( source_count ), {",\n"} );
+ $v_string( &C, {" .source_static = \""} );
+
+ for( u32 i=0; i<source_count; i ++ )
{
- sorted = 1;
-
- for( u32 j=1; j<stretchy_count( &event->users ); j ++ )
+ u32 context_block = _assembly_kv_file_context( sources[i] );
+ u32 temp_frame = _start_temporary_frame();
{
- struct hook_user *j1 = stretchy_get( &event->users, j ),
- *j0 = stretchy_get( &event->users, j-1 );
- if( j1->affinity < j0->affinity )
+ struct stream path_string;
+ stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate );
+ $v_string( &path_string, {keyvalues_read_string( &_assembly, context_block, "folder", NULL )}, {"/"},
+ {keyvalues_value( &_assembly, sources[i], NULL )} );
+
+ struct stream source_file;
+ ASSERT_CRITICAL( stream_open_file( &source_file, string_get( &path_string ), k_stream_read ) );
+ u32 l;
+ const c8 *source = stream_read_all( &source_file, _temporary_stack_allocator(), 4, &l );
+ for( u32 k=0; k<l; k ++ )
{
- struct hook_user temp = *j0;
- *j0 = *j1;
- *j1 = temp;
- sorted = 0;
+ c8 c = source[k];
+ if( c == '\n' ) $v_string( &C, {"\\n"} );
+ else string_append_c8( &C, c );
}
}
+ _end_temporary_frame( temp_frame );
}
- $v_string( &source, {returns}, {" (*_event_"}, {alias}, {"_subscribers[])( "}, {prototype}, {" ) = \n{\n"} );
- for( u32 j=0; j<stretchy_count( &event->users ); j ++ )
+ $v_string( &C, {"\",\n"} );
+ $v_string( &C, {" .source_uniforms = \""} );
+
+ u32 uniform_it = 0;
+ while( keyvalues_foreach( &_assembly, &uniform_it, subshader_it, "uniform" ) )
{
- struct hook_user *user = stretchy_get( &event->users, j );
- $v_string( &source, {" "}, {string_get(&user->function_name)}, {",\n"} );
+ struct uniform_info *uniform = stretchy_append( &uniforms );
+ uniform->kv = uniform_it;
+ uniform->glsl_index = -1;
+ uniform->type = keyvalues_read_string( &_assembly, uniform_it, "type", NULL );
+ uniform->alias = keyvalues_read_string( &_assembly, uniform_it, "alias", NULL );
+ uniform->shader_name = shader_name;
+ ASSERT_CRITICAL( uniform->type && uniform->alias );
+
+ for( i32 i=0; i<ARRAY_COUNT( glsl_list ); i ++ )
+ {
+ if( compare_buffers( glsl_list[i].type, 0, uniform->type, 0 ) )
+ {
+ uniform->glsl_index = i;
+ break;
+ }
+ }
+ ASSERT_CRITICAL( uniform->glsl_index != -1 );
+ uniform_count ++;
+
+ $v_string( &C, {"uniform "}, {uniform->type}, {" "}, {uniform->alias}, {";\\n"} );
}
- $v_string( &source, {" NULL\n};\n\n"} );
+ $v_string( &C, {"\",\n"}, {" },\n"} );
+ subshader_count ++;
}
- stream_close( &source );
- stream_close( &header );
+ $v_string( &C, {" },\n"} );
+ $v_string( &C, {" .uniform_count = "}, $unsigned( uniform_count ), {",\n"} );
+ $v_string( &C, {" .subshader_count = "}, $unsigned( subshader_count ), {",\n"} );
+ $v_string( &C, {" },\n"} );
+ }
+ $v_string( &C, {"};\n"} );
+ $v_string( &H, {" k_shader_count\n};\n\n"} );
+
- $v_string( &_metacompiler.source_list, {" generated/hooks.c \\\n"} );
+ for( u32 i=0; i<stretchy_count(&uniforms); i ++ )
+ {
+ struct uniform_info *uniform = stretchy_get( &uniforms, i );
+ struct glsl_trans *trans = &glsl_list[ uniform->glsl_index ];
+ $v_string( &H, {"void _shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias}, {"( "},{trans->args},{" );\n"} );
+ $v_string( &C, {"void _shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias}, {"( "},{trans->args},{" )"},
+ {"{ "},{trans->call},{"( _uniform_locations["}, {"k_shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias},
+ {"], "},{trans->end},{" ); }\n"});
}
- $log( $info, {"Generating shader header"} );
- if( _shaders.using )
+ $v_string( &C, {"const c8 *_uniform_aliases[] = \n{\n"} );
+ $v_string( &H, {"enum shader_uniform_id\n{\n"} );
+ for( u32 i=0; i<stretchy_count(&uniforms); i ++ )
{
- struct stream header, source;
- ASSERT_CRITICAL( stream_open_file( &header, "generated/shaders.h", k_stream_write ) );
- ASSERT_CRITICAL( stream_open_file( &source, "generated/shaders.c", k_stream_write ) );
+ struct uniform_info *uniform = stretchy_get( &uniforms, i );
- $v_string( &header, {"enum shader_id\n{\n"}, {string_get( &_shaders.shader_enum )}, {" k_shader_count\n};\n"} );
- $v_string( &header, {"enum shader_uniform_id\n{\n"}, {string_get( &_shaders.uniform_enum )}, {" k_shader_uniform_count\n};\n"} );
- $v_string( &header, {string_get( &_shaders.uniform_func_protos )} );
+ $v_string( &H, {" k_shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias}, {",\n"} );
+ $v_string( &C, {" [k_shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias}, {"] = \""}, {uniform->alias},{"\",\n"} );
+ }
+ $v_string( &H, {" k_shader_uniform_count\n};\n\n"} );
+ $v_string( &C, {"};\n"} );
- $v_string( &source, {"const c8 *_uniform_aliases[] = \n{\n"}, {string_get( &_shaders.uniform_aliases )}, {"};\n"} );
+ stream_close( &H );
+ stream_close( &C );
- $v_string( &source, {"struct shader _shader_definitions[] = {\n"} );
- for( u32 i=0; i<stretchy_count( &_shaders.shaders ); i ++ )
- {
- struct shader *shader = stretchy_get( &_shaders.shaders, i );
- $v_string( &source, {" [k_shader_"}, {string_get( &shader->name )}, {"] = \n {\n"} );
- $v_string( &source, {" .name = \""}, {string_get( &shader->name )}, {"\",\n"} );
- $v_string( &source, {" .subshader_count = "}, $unsigned( shader->subshader_count ), {",\n"} );
- $v_string( &source, {" .uniform_start = "}, $unsigned( shader->uniform_start ), {",\n"} );
- $v_string( &source, {" .uniform_count = "}, $unsigned( shader->uniform_count ), {",\n"} );
- $v_string( &source, {" .subshaders = (struct subshader[])\n {\n"} );
-
- for( u32 j=0; j<shader->subshader_count; j ++ )
- {
- struct subshader *subshader = &shader->subshaders[ j ];
- $v_string( &source, {" {\n"} );
- $v_string( &source, {" .type = "},
- { (const c8 *[]){ [k_subshader_vertex] = "k_subshader_vertex",
- [k_subshader_fragment] = "k_subshader_fragment",
- [k_subshader_geometry] = "k_subshader_geometry" }[ subshader->type ] }, {",\n"} );
- $v_string( &source, {" .source_count = "}, $unsigned( subshader->debug_source_count ), {",\n"} );
- $v_string( &source, {" .source_list = (const c8 *[])\n"} );
- $v_string( &source, {" {\n"} );
- $v_string( &source, {string_get( &subshader->debug_source_list )} );
- $v_string( &source, {" },\n"} );
- $v_string( &source, {" .source_static = \""} );
-
- for( u32 k=0; k<subshader->static_source.offset; k ++ )
- {
- c8 c = subshader->static_source.buffer_read[ k ];
- if( c == '\n' ) $v_string( &source, {"\\n"} );
- else string_append_c8( &source, c );
- }
- $v_string( &source, {"\",\n"} );
- $v_string( &source, {" .source_uniforms = \""}, {string_get( &subshader->uniform_source )}, {"\",\n"} );
- $v_string( &source, {" },\n"} );
- }
+ stretchy_free( &uniforms );
+}
- $v_string( &source, {" },\n"} );
- $v_string( &source, {" },\n"} );
- }
- $v_string( &source, {"};\n"} );
- $v_string( &source, {string_get( &_shaders.uniform_funcs )} );
+void _codegen_inputs(void)
+{
+ $log( $info, {"[CODEGEN] input.h/input.c"} );
+ struct stream C, H;
+ stream_open_file( &H, "generated/input.h", k_stream_write );
+ stream_open_file( &C, "generated/input.c", k_stream_write );
+
+ $v_string( &H, {"enum input_layer_id\n{\n"} );
+ u32 layer_it = 0;
+ while( keyvalues_foreach( &_assembly, &layer_it, 0, "input_layer" ) )
+ {
+ const c8 *layer_name = keyvalues_read_string( &_assembly, layer_it, "name", NULL );
+ ASSERT_CRITICAL( layer_name );
+ $v_string( &H, {" k_input_layer_"}, {layer_name}, {",\n"} );
+ }
+ $v_string( &H, {" k_input_layer_count\n};\n"} );
+
+ $v_string( &H, {"enum input_id\n{\n"} );
+ $v_string( &C, {"struct input_info _input_infos[k_input_count] = \n{\n"} );
+ u32 input_it = 0;
+ while( keyvalues_foreach( &_assembly, &input_it, 0, "input" ) )
+ {
+ const c8 *name = keyvalues_read_string( &_assembly, input_it, "name", NULL );
+ const c8 *type = keyvalues_read_string( &_assembly, input_it, "type", NULL );
+ const c8 *layer_mask = keyvalues_read_string( &_assembly, input_it, "layer_mask", NULL );
+ ASSERT_CRITICAL( name && type );
+
+ $v_string( &H, {" k_input_"},{type},{"_"}, {name}, {",\n"} );
+ $v_string( &C, {" [k_input_"},{type},{"_"},{name},{"]=\n {\n"} );
+ $v_string( &C, {" .name = \""},{name},{"\",\n"} );
+ $v_string( &C, {" .type = k_input_type_"},{type},{",\n"} );
+ if( layer_mask )
+ $v_string( &C, {" .layer_mask = 1<<k_input_layer_"}, {layer_mask}, {",\n"} );
+ $v_string( &C, {" },\n"} );
+ }
+ $v_string( &H, {" k_input_count\n};\n"} );
+ $v_string( &C, {"};\n"} );
+
+ stream_close( &H );
+ stream_close( &C );
+}
+
+void _codegen_console(void)
+{
+ $log( $info, {"[CODEGEN] console.h/console.c"} );
+ struct stream C, H;
+ stream_open_file( &H, "generated/console.h", k_stream_write );
+ stream_open_file( &C, "generated/console.c", k_stream_write );
+
+ $v_string( &C, {"static struct console_command _console_commands[] = \n{\n"} );
+
+ u32 ccmd_it = 0;
+ while( keyvalues_foreach( &_assembly, &ccmd_it, 0, "ccmd" ) )
+ {
+ const c8 *name = keyvalues_read_string( &_assembly, ccmd_it, "name", NULL );
+ const c8 *function = keyvalues_read_string( &_assembly, ccmd_it, "function", NULL );
+ ASSERT_CRITICAL( name && function );
+
+ $v_string( &H, {"i32 "}, {function}, {"( struct console_arguments *args );\n"} );
+ $v_string( &C, {" {\n .alias = \""}, {name}, {"\",\n .fn = "}, {function}, {"\n },\n"} );
+ }
+ $v_string( &C, {"};\n\n"} );
+
+ stream_close( &H );
+ stream_close( &C );
+}
- stream_close( &source );
- stream_close( &header );
+void _codegen_threads(void)
+{
+ $log( $info, {"[CODEGEN] threads.h/threads.c"} );
+ struct stream C, H;
+ stream_open_file( &H, "generated/threads.h", k_stream_write );
+ stream_open_file( &C, "generated/threads.c", k_stream_write );
+
+ $v_string( &H, {"enum thread_id\n{\n"} );
+ $v_string( &C, {"struct thread_info _thread_infos[k_thread_count] = \n{\n"} );
+
+ u32 thread_it = 0;
+ while( keyvalues_foreach( &_assembly, &thread_it, 0, "thread" ) )
+ {
+ const c8 *name = keyvalues_read_string( &_assembly, thread_it, "name", NULL );
+ ASSERT_CRITICAL( name );
+
+ $v_string( &H, {" k_thread_"},{name}, {",\n"} );
+
+ $v_string( &C, {" [k_thread_"},{name},{"]=\n {\n"} );
+ $v_string( &C, {" .name = \""}, {name}, {"\",\n"} );
+
+ u32 queue_size_m = 0;
+ if( keyvalues_read_u32s( &_assembly, thread_it, "queue_size_m", NULL, &queue_size_m, 1 ) )
+ $v_string( &C, {" .queue_size_m = "}, $unsigned(queue_size_m), {",\n"} );
+
+ u32 flag_kv = keyvalues_get( &_assembly, thread_it, "flag", 0 );
+ if( flag_kv )
+ $v_string( &C, {" .flags = "} );
+
+ u32 flag_count = 0;
+ while( flag_kv )
+ {
+ const c8 *flag_name = keyvalues_value( &_assembly, flag_kv, NULL );
+ $v_string( &C, {flag_count? "|": ""}, {"THREAD_FLAG_"}, {flag_name} );
+ flag_kv = keyvalues_get( &_assembly, flag_kv, "flag", 1 );
+ flag_count ++;
+ }
+ if( flag_count )
+ $v_string( &C, {",\n"} );
+ $v_string( &C, {" },\n"} );
}
- /* main */
+ $v_string( &H, {" k_thread_count\n};\n"} );
+ $v_string( &C, {"};\n"} );
+
+ stream_close( &H );
+ stream_close( &C );
+}
+
+void _default_config_gen(void)
+{
+ $log( $info, {"[CODEGEN] default.cfg"} );
+
+ struct shell_command *cmd = _shell_command();
+ $v_string( &cmd->line, {"mkdir -p "}, {string_get( &_metacompiler.folder )}, {"/cfg"} );
+ _shell_run( cmd );
+
u32 temp_frame = _start_temporary_frame();
{
- struct stream command_string;
- stream_open_stack( &command_string, _temporary_stack_allocator(), k_stream_null_terminate );
- $v_string( &command_string, {"taskset -c 0-"}, $unsigned(processors),
- {" zig cc -Wall -Wno-unused-function -std=gnu99 -D_REENTRANT -lm \\\n"},
- {" -include \"source/types.h\" \\\n"} );
+ struct stream config_path;
+ stream_open_stack( &config_path, _temporary_stack_allocator(), k_stream_null_terminate );
+ $v_string( &config_path, {string_get( &_metacompiler.folder )}, {"/cfg/default.cfg"} );
+
+ struct stream config_file;
+ ASSERT_CRITICAL( stream_open_file( &config_file, string_get( &config_path ), k_stream_write ) );
+
+ u32 config_it = 0;
+ while( keyvalues_foreach( &_assembly, &config_it, 0, "config" ) )
+ $v_string( &config_file, {keyvalues_value( &_assembly, config_it, NULL ) }, {"\n"} );
+
+ stream_close( &config_file );
+ }
+ _end_temporary_frame( temp_frame );
+}
+
+i32 main( i32 argc, const c8 *argv[] )
+{
+ VG_PRE_MAIN;
+
+ /* Get name of target and do a few checks */
+ i32 name_start = buffer_last_index( _option_file_path, '/', 0 );
+ if( name_start == -1 ) name_start = 0;
+ i32 name_end = buffer_last_index( _option_file_path+name_start, '.', 0 );
+
+ b8 valid_kv = 1;
+ if( name_end == -1 )
+ valid_kv = 0;
+ else if( !compare_buffers( ".kv", 0, _option_file_path+name_start+name_end, 0 ) )
+ valid_kv = 0;
+
+ if( !valid_kv )
+ {
+ $log( $fatal, {"Target file must be a text KV file"} );
+ _fatal_exit();
+ }
+
+ struct stream open_test;
+ if( stream_open_file( &open_test, _option_file_path, k_stream_read ) )
+ stream_close( &open_test );
+ else
+ {
+ $log( $fatal, {"Can't open for reading: '"}, {_option_file_path}, {"'"} );
+ _fatal_exit();
+ }
+
+ stream_open_auto( &_metacompiler.name, k_stream_null_terminate );
+ string_append( &_metacompiler.name, _option_file_path+name_start, name_end );
+
+ /* Create tripple string from configuration */
+ stream_open_auto( &_metacompiler.tripple, k_stream_null_terminate );
+ $v_string( &_metacompiler.tripple, {architecture_names[_option_arch]}, {"-"}, {platform_names[_option_platform]} );
+ if( _option_platform == k_platform_linux )
+ {
+ $v_string( &_metacompiler.tripple, {"-gnu"} );
+ if( _option_libc != k_libc_version_native )
+ $v_string( &_metacompiler.tripple, {"."}, {libc_names[_option_libc]} );
+ }
+
+ /* Create output folder (relative to target file) from tripple + name */
+ c8 *output_folder = realpath( _option_file_path, NULL );
+ if( !output_folder )
+ {
+ $log( $fatal, {"While making output folder, realpath of '"}, {_option_file_path}, {"' failed. "}, $errno() );
+ _fatal_exit();
+ }
+
+ i32 s = buffer_last_index( output_folder, '/', 0 );
+ if( s != -1 )
+ output_folder[s] = '\0';
+
+ stream_open_auto( &_metacompiler.folder, k_stream_null_terminate );
+ $v_string( &_metacompiler.folder, {output_folder}, {"/bin/"},
+ {string_get( &_metacompiler.name )}, {"-"}, {string_get( &_metacompiler.tripple )} );
+
+ free(output_folder);
+ $log( $info, {"Target: "}, {string_get( &_metacompiler.name )}, {"\n"},
+ {" configuration: "}, {string_get( &_metacompiler.tripple )}, {_option_shared?" SO":" EXE"}, {"\n"},
+ {" output: "}, {string_get( &_metacompiler.folder )}, {"\n"},
+ {" options: "},
+ {_option_use_tsan? "TSAN ": ""},
+ {_option_use_asan? "ASAN ": ""},
+ {"O"}, $unsigned( _option_optimise ), {" "},
+ {_option_no_pdb? "NO-PDB ": ""} );
+
+ /* Assemble target into main assembly */
+ struct stack_allocator stack;
+ stack_init( &stack, NULL, BYTES_MB(16), "Assembly KV" );
+ keyvalues_init( &_assembly, &stack );
+
+ u32 info_block = keyvalues_append_frame( &_assembly, 0, "vgc" );
+ keyvalues_append_string( &_assembly, info_block, "version", "0.2" );
+ keyvalues_append_string( &_assembly, info_block, "name", string_get( &_metacompiler.name ) );
+ keyvalues_append_string( &_assembly, info_block, "tripple", string_get( &_metacompiler.tripple ) );
+ keyvalues_append_string( &_assembly, info_block, "folder", string_get( &_metacompiler.folder ) );
+
+ if( _option_platform == k_platform_linux ) _metacompiler.enabled_features[0] = "linux";
+ if( _option_platform == k_platform_windows ) _metacompiler.enabled_features[0] = "windows";
+ _assemble_kv_file( _option_file_path, 0, 1 );
+
+ if( _option_assemble_only )
+ {
+ keyvalues_write_stream( &_assembly, &_assemble_file, 0, 0 );
+ stream_close( &_assemble_file );
+ _normal_exit();
+ }
+
+ {
+ struct shell_command *cmd = _shell_command();
+ $v_string( &cmd->line, {"mkdir -p "}, {string_get( &_metacompiler.folder )} );
+ _shell_run( cmd );
+ }
+
+ {
+ struct shell_command *cmd = _shell_command();
+ $v_string( &cmd->line, {"mkdir -p generated"} );
+ _shell_run( cmd );
+ }
+
+ _codegen_hooks();
+ _codegen_shaders();
+ _codegen_inputs();
+ _codegen_console();
+ _codegen_threads();
+
+ {
+ struct shell_command *cmd = _shell_command();
+ $v_string( &cmd->line, {"taskset -c 0-"}, $unsigned(_option_processors),
+ {" zig cc -Wall -Wno-unused-function -std=gnu99 -D_REENTRANT -lm \\\n"},
+ {" -include \"vg/source/types.h\" \\\n"} );
#if 0
- if( use_tsan ) $v_string( &command_string, {" -fsanitize=thread -lasan -L/lib/x86_64-linux-gnu \\\n"} );
- if( use_asan ) $v_string( &command_string, {" -fsanitize=address -lasan -L/lib/x86_64-linux-gnu \\\n"} );
+ if( _option_use_tsan ) $v_string( &cmd->line, {" -fsanitize=thread -lasan -L/lib/x86_64-linux-gnu \\\n"} );
+ if( _option_use_asan ) $v_string( &cmd->line, {" -fsanitize=address -lasan -L/lib/x86_64-linux-gnu \\\n"} );
#else
- if( use_tsan ) $v_string( &command_string, {" -fsanitize=thread -lasan -L/usr/lib \\\n"} );
- if( use_asan ) $v_string( &command_string, {" -fsanitize=address -lasan -L/usr/lib \\\n"} );
+ if( _option_use_tsan ) $v_string( &cmd->line, {" -fsanitize=thread -lasan -L/usr/lib \\\n"} );
+ if( _option_use_asan ) $v_string( &cmd->line, {" -fsanitize=address -lasan -L/usr/lib \\\n"} );
#endif
- $v_string( &command_string, { " -O" }, $unsigned( optimise ) );
- if( optimise == 0 ) $v_string( &command_string, { " -ggdb" } );
- $v_string( &command_string, {" \\\n"} );
+ $v_string( &cmd->line, { " -O" }, $unsigned( _option_optimise ) );
+ if( _option_optimise == 0 ) $v_string( &cmd->line, { " -ggdb" } );
+ $v_string( &cmd->line, {" \\\n"} );
- $v_string( &command_string, {shared? " -shared -fPIC ": " "} );
- if( platform == k_platform_windows )
+ $v_string( &cmd->line, {_option_shared? " -shared -fPIC ": " "} );
+ if( _option_platform == k_platform_windows )
{
- $v_string( &command_string, {" -target "}, {string_get(&tripple_string)}, {" \\\n"} );
- if( !shared )
- $v_string( &command_string, {"-Wl,--subsystem=windows "} );
+ $v_string( &cmd->line, {" -target "}, {string_get(&_metacompiler.tripple)}, {" \\\n"} );
+ if( !_option_shared )
+ $v_string( &cmd->line, {"-Wl,--subsystem=windows "} );
- if( no_pdb )
- $v_string( &command_string, {"-Wl,/pdb:/dev/null "} );
+ if( _option_no_pdb )
+ $v_string( &cmd->line, {"-Wl,/pdb:/dev/null "} );
}
- $v_string( &command_string, {"\\\n"} );
-
- $v_string( &command_string, {" -fno-sanitize=undefined \\\n"} );
- $v_string( &command_string, {" -fkeep-static-consts -fkeep-persistent-storage-variables \\\n"} );
-
- $v_string( &command_string, {" -I. \\\n"} );
- $v_string( &command_string, {string_get( &_metacompiler.include_path_list )} );
- $v_string( &command_string, {string_get( &_metacompiler.define_list )} );
- $v_string( &command_string, {string_get( &_metacompiler.source_list )} );
+ $v_string( &cmd->line, {"\\\n"} );
- $v_string( &command_string, {"-o "}, {string_get( &folder_string )}, {"/"}, {_metacompiler.project_name} );
- if( platform == k_platform_windows ) $v_string( &command_string, {shared? ".dll ": ".exe "} );
- else $v_string( &command_string, {shared? ".so ": " "} );
+ $v_string( &cmd->line, {" -fno-sanitize=undefined \\\n"} );
+ $v_string( &cmd->line, {" -fkeep-static-consts -fkeep-persistent-storage-variables \\\n"} );
- system_call( string_get( &command_string ) );
+ $v_string( &cmd->line, {" -I. \\\n"} );
- if( _console.using )
+ u32 compile_it = 0;
+ while( keyvalues_foreach( &_assembly, &compile_it, 0, NULL ) )
{
- u32 temp_frame2 = _start_temporary_frame();
- struct stream command_string;
- stream_open_stack( &command_string, _temporary_stack_allocator(), k_stream_null_terminate );
- $v_string( &command_string, {"mkdir -p "}, {string_get( &folder_string )}, {"/cfg"} );
- system_call( string_get( &command_string ) );
-
- struct stream config_path;
- stream_open_stack( &config_path, _temporary_stack_allocator(), k_stream_null_terminate );
- $v_string( &config_path, {string_get( &folder_string )}, {"/cfg/default.cfg"} );
-
- struct stream config_file;
- ASSERT_CRITICAL( stream_open_file( &config_file, string_get( &config_path ), k_stream_write ) );
- $v_string( &config_file, {string_get( &_metacompiler.configuration )} );
- stream_close( &config_file );
-
- _end_temporary_frame( temp_frame2 );
+ const c8 *key = keyvalues_key( &_assembly, compile_it, NULL );
+ b8 include = compare_buffers( key, 0, "include", 0 ),
+ link = compare_buffers( key, 0, "link", 0 ),
+ link_path = compare_buffers( key, 0, "link_folder", 0 ),
+ define = compare_buffers( key, 0, "define", 0 ),
+ add = compare_buffers( key, 0, "add", 0 );
+
+ if( include || link_path || add )
+ {
+ u32 context_block = _assembly_kv_file_context( compile_it );
+ if( include ) $v_string( &cmd->line, {" -I"} );
+ if( link_path ) $v_string( &cmd->line, {" -L"} );
+ if( add ) $v_string( &cmd->line, {" "} );
+ $v_string( &cmd->line, {keyvalues_read_string( &_assembly, context_block, "folder", NULL )},
+ {"/"}, {keyvalues_value( &_assembly, compile_it, NULL )}, {" \\\n"} );
+ }
+ else if( define || link )
+ {
+ if( define ) $v_string( &cmd->line, {" -D"} );
+ if( link ) $v_string( &cmd->line, {" -l"} );
+ $v_string( &cmd->line, {keyvalues_value( &_assembly, compile_it, NULL )}, {" \\\n"} );
+ }
}
+ $v_string( &cmd->line, {" generated/hooks.c \\\n"} );
- for( u32 i=0; i< stretchy_count( &_commands.shell ); i ++ )
- {
- struct shell_command *command = stretchy_get( &_commands.shell, i );
- system_call( string_get( &command->command ) );
- }
+ $v_string( &cmd->line, {"-o "}, {string_get( &_metacompiler.folder )}, {"/"}, {string_get(&_metacompiler.name)} );
+ if( _option_platform == k_platform_windows ) $v_string( &cmd->line, {_option_shared? ".dll ": ".exe "} );
+ else $v_string( &cmd->line, {_option_shared? ".so ": " "} );
+ _shell_run( cmd );
}
- _end_temporary_frame( temp_frame );
+
+ _default_config_gen();
return 0;
}