stepcode/cmake/schema_scanner/schemaScanner.cc

336 lines
12 KiB
C++

/** \file schemaScanner.cc
* This file, along with part of libexpress, are compiled (at configure time)
* into a static executable. This executable is a schema scanner that is used
* by CMake to determine what files exp2cxx will create. Otherwise, we'd need
* to use a few huge files - there is no other way to tell CMake what the
* generated files will be called.
*/
/* WARNING
* If you modify this file, you must re-run cmake. It doesn't seem to be possible
* to re-run cmake automatically, as CMake's configure_file() gets confused by
* the '${' and '}' in writeLists() below.
*/
extern "C" {
# include "expparse.h"
# include "expscan.h"
# include "express/scope.h"
# include "genCxxFilenames.h"
# include "sc_mkdir.h"
# include <string.h>
# if defined( _WIN32 ) || defined ( __WIN32__ )
# include <direct.h>
# define getcwd _getcwd
# else
# include <unistd.h>
# endif
}
#include <string>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <iostream>
int multiple_inheritance = 0;
using std::string;
using std::stringstream;
using std::endl;
using std::ofstream;
using std::cerr;
using std::cout;
/** \return true for types that exp2cxx won't generate code for */
bool notGenerated( const Type t ) {
switch( TYPEget_body( t )->type ) {
case integer_:
case real_:
case string_:
case binary_:
case boolean_:
case number_:
case logical_:
case aggregate_:
case bag_:
case set_:
case list_:
case array_:
return true;
default:
break;
}
/* from addRenameTypedefs() in multpass.c - check for renamed
* enums and selects - exp2cxx prints typedefs for them */
if( ( TYPEis_enumeration( t ) || TYPEis_select( t ) ) && ( TYPEget_head( t ) ) ) {
return true;
}
return false;
}
/** \return a short name for the schema
* this name is intended to be unique, but no special care is put into its generation
* the official schema name seems most likely to not be unique, but it is generally the longest
*
* picks the shortest of:
* * the name of the dir containing the file (if in sc/data),
* * the name of the file containing the schema (minus extension),
* * the official name of the schema, passed in arg 'longName'
*
* TODO: maybe also consider an acronym, such that 'test_array_bounds_expr' becomes 'tabe', 'TABE', 'T_A_B_E', or similar
*/
string makeShortName( const char * longName ) {
//input_filename is path to file. we will extract dir name and file name from it.
string dirname = input_filename, filename = input_filename, schname = longName;
const char slash = '/';
//for filename, get rid of dir name(s), if any, as well as the extension
size_t sl = filename.rfind( slash );
if( sl != string::npos ) {
filename.erase( 0, sl + 1 );
}
size_t dot = filename.rfind( '.' );
if( dot != string::npos ) {
filename.erase( dot );
}
//for dirname, get rid of the filename and ensure it's in data/
sl = dirname.rfind( slash );
if( sl != string::npos ) {
dirname.erase( sl );
}
const char * dat = "data";
size_t data = dirname.find( dat );
if( ( data == string::npos ) || ( dirname[ data + strlen( dat ) ] != slash ) ) {
//doesn't contain 'data/'. clear it so it'll be ignored.
dirname.clear();
} else {
//get rid of all but last dir name
dirname.erase( 0, dirname.rfind( slash ) + 1 );
}
//use dir name if it's at least 3 chars and shorter than file name
//length requirement gets rid of short, undescriptive dir names - including '.' and '..'
if( ( dirname.size() > 2 ) && ( dirname.size() < filename.size() ) ) {
filename = dirname;
}
if( strlen( longName ) < filename.size() ) {
filename = longName;
}
filename.insert( 0, "sdai_" );
return filename;
}
/** write a CMakeLists.txt file for the schema; print its directory to stdout for CMake's add_subdirectory() command */
void writeLists( const char * schemaName, stringstream & eh, stringstream & ei, int ecount,
stringstream & th, stringstream & ti, int tcount ) {
string shortName = makeShortName( schemaName );
if( mkDirIfNone( shortName.c_str() ) < 0 ) {
cerr << "Error creating directory " << shortName << " at " << __FILE__ << ":" << __LINE__;
perror( 0 );
exit( EXIT_FAILURE );
}
size_t nameLen = strlen( schemaName );
string schema_upper( nameLen, char() );
for( size_t i = 0; i < nameLen; ++i) {
schema_upper[i] = toupper(schemaName[i]);
}
string cmListsPath = shortName;
cmListsPath += "/CMakeLists.txt";
ofstream cmLists;
cmLists.open( cmListsPath.c_str() );
if( !cmLists.good() ) {
cerr << "error opening file " << cmListsPath << " - exiting." << endl;
exit( EXIT_FAILURE );
}
cmLists << "# ----- GENERATED FILE -----" << endl;
cmLists << "# ----- Do not edit! -----" << endl << endl;
cmLists << "# schema name: " << schemaName << endl;
cmLists << "# (short name: " << shortName << ")" << endl;
cmLists << "# " << ecount << " entities, " << tcount << " types" << endl << endl;
cmLists << "# targets, logic, etc are within a set of macros shared by all schemas" << endl;
cmLists << "include(${SC_CMAKE_DIR}/SC_CXX_schema_macros.cmake)" << endl;
// * 2 for headers, + 10 other files
cmLists << "set(" << shortName << "_file_count " << ( ( ecount + tcount ) * 2 ) + 10 << ")" << endl << endl;
cmLists << "PROJECT(" << shortName << ")" << endl;
cmLists << "# list headers so they can be installed - entity, type, misc" << endl;
cmLists << "set(" << shortName << "_entity_hdrs" << endl;
cmLists << eh.str();
cmLists << " )" << endl << endl;
cmLists << "set(" << shortName << "_type_hdrs" << endl;
cmLists << th.str();
cmLists << " )" << endl << endl;
cmLists << "set(" << shortName << "_misc_hdrs" << endl;
cmLists << " Sdaiclasses.h schema.h" << endl;
cmLists << " Sdai" << schema_upper << "Names.h" << endl;
cmLists << " Sdai" << schema_upper << ".h" << endl;
cmLists << " )" << endl << endl;
cmLists << "# install all headers" << endl;
cmLists << "set(all_headers ${" << shortName << "_entity_hdrs} ${" << shortName << "_type_hdrs} ${" << shortName << "_misc_hdrs})" << endl;
cmLists << "foreach( header_file ${all_headers} )" << endl;
cmLists << " set(curr_dir)" << endl;
cmLists << " get_filename_component(curr_dir ${header_file} PATH)" << endl;
cmLists << " get_filename_component(curr_name ${header_file} NAME)" << endl;
cmLists << " if (curr_dir)" << endl;
cmLists << " install( FILES ${header_file} DESTINATION \"include/schemas/" << shortName << "/${curr_dir}\" )" << endl;
cmLists << " else (curr_dir)" << endl;
cmLists << " install( FILES ${header_file} DESTINATION \"include/schemas/" << shortName << "\" )" << endl;
cmLists << " endif (curr_dir)" << endl;
cmLists << "endforeach()" << endl;
cmLists << "# implementation files - 3 lists" << endl << endl;
cmLists << "# unity build: #include small .cc files to reduce the number" << endl;
cmLists << "# of translation units that must be compiled" << endl;
cmLists << "if(SC_UNITY_BUILD)" << endl << " # turns off include statements within type and entity .cc's - the unity T.U.'s include a unity header" << endl;
cmLists << " add_definitions( -DSC_SDAI_UNITY_BUILD)" << endl;
cmLists << " set(" << shortName << "_entity_impls Sdai" << schema_upper << "_unity_entities.cc)" << endl;
cmLists << " set(" << shortName << "_type_impls Sdai" << schema_upper << "_unity_types.cc)" << endl;
cmLists << "else(SC_UNITY_BUILD)" << endl;
cmLists << " set(" << shortName << "_entity_impls" << endl;
cmLists << ei.str();
cmLists << " )" << endl << endl;
cmLists << " set(" << shortName << "_type_impls" << endl;
cmLists << ti.str();
cmLists << " )" << endl;
cmLists << "endif(SC_UNITY_BUILD)" << endl << endl;
cmLists << "set( " << shortName << "_misc_impls" << endl;
cmLists << " SdaiAll.cc compstructs.cc schema.cc" << endl;
cmLists << " Sdai" << schema_upper << ".cc" << endl;
cmLists << " Sdai" << schema_upper << ".init.cc )" << endl << endl;
cmLists << "set(schema_target_files ${" << shortName << "_entity_impls} " << "${" << shortName << "_type_impls} " << "${" << shortName << "_misc_impls})" << endl;
cmLists << "SCHEMA_TARGETS(\"" << input_filename << "\" \"" << schemaName << "\"" << endl;
cmLists << " \"${schema_target_files}\")" << endl;
cmLists.close();
char pwd[BUFSIZ] = {0};
if( getcwd( pwd, BUFSIZ ) ) {
cout << pwd << "/" << shortName << endl;
} else {
cerr << "Error encountered by getcwd() for " << shortName << " - exiting. Error was ";
perror( 0 );
exit( EXIT_FAILURE );
}
}
void printSchemaFilenames( Schema sch ){
const int numColumns = 2;
const int colWidth = 75;
const char * tab = " ";
stringstream typeHeaders, typeImpls, entityHeaders, entityImpls;
typeHeaders << tab;
typeImpls << tab;
entityHeaders << tab;
entityImpls << tab;
int ecount = 0, tcount = 0;
DictionaryEntry de;
Generic x;
filenames_t fn;
DICTdo_init( sch->symbol_table, &de );
while( 0 != ( x = DICTdo( &de ) ) ) {
switch( DICT_type ) {
case OBJ_ENTITY:
fn = getEntityFilenames( ( Entity ) x );
entityHeaders << std::left << std::setw( colWidth ) << fn.header << " ";
entityImpls << std::left << std::setw( colWidth ) << fn.impl << " ";
++ecount;
if( ( ecount % numColumns ) == 0 ) {
// columns
entityHeaders << endl << tab;
entityImpls << endl << tab;
}
break;
case OBJ_TYPE: {
Type t = ( Type ) x;
if( TYPEis_enumeration( t ) && ( TYPEget_head( t ) ) ) {
/* t is a renamed enum type, for which exp2cxx
* will print a typedef in an existing file */
break;
}
if( notGenerated( t ) ) {
/* skip builtin types */
break;
}
fn = getTypeFilenames( t );
typeHeaders << std::left << std::setw( colWidth ) << fn.header << " ";
typeImpls << std::left << std::setw( colWidth ) << fn.impl << " ";
++tcount;
if( ( tcount % numColumns ) == 0 ) {
typeHeaders << endl << tab;
typeImpls << endl << tab;
}
break;
}
/* case OBJ_FUNCTION:
* case OBJ_PROCEDURE:
* case OBJ_RULE: */
default:
/* ignore everything else */
/* TODO: if DEBUG is defined, print the names of these to stderr */
break;
}
}
//write the CMakeLists.txt
writeLists( sch->symbol.name, entityHeaders, entityImpls, ecount, typeHeaders, typeImpls, tcount );
}
int main( int argc, char ** argv ) {
/* TODO init globals! */
Schema schema;
DictionaryEntry de;
/* copied from fedex.c */
Express model;
if( ( argc != 2 ) || ( strlen( argv[1] ) < 1 ) ) {
fprintf( stderr, "\nUsage: %s file.exp\nOutput: a CMakeLists.txt to build the schema,", argv[0] );
fprintf( stderr, " containing file names for entities, types, etc\n" );
fprintf( stderr, "also prints (to stdout) the absolute path to the directory CMakeLists.txt was created in\n" );
exit( EXIT_FAILURE );
}
EXPRESSprogram_name = argv[0];
input_filename = argv[1];
EXPRESSinitialize();
model = EXPRESScreate();
EXPRESSparse( model, ( FILE * )0, input_filename );
if( ERRORoccurred ) {
EXPRESSdestroy( model );
exit( EXIT_FAILURE );
}
EXPRESSresolve( model );
if( ERRORoccurred ) {
int result = EXPRESS_fail( model );
EXPRESScleanup();
EXPRESSdestroy( model );
exit( result );
}
DICTdo_type_init( model->symbol_table, &de, OBJ_SCHEMA );
while( 0 != ( schema = ( Schema )DICTdo( &de ) ) ) {
printSchemaFilenames( schema );
}
EXPRESSdestroy( model );
exit( EXIT_SUCCESS );
}