From b3bb81cfd0c300f48576747c6099474051296900 Mon Sep 17 00:00:00 2001 From: starkos Date: Fri, 9 May 2008 15:19:56 +0000 Subject: [PATCH] Initial implementation of files() with Visual Studio support (r359:369) --- src/action/action.c | 98 +++++++- src/action/action.h | 30 +++ src/action/tests/action_tests.h | 12 + .../vs200x/tests/vs2002_config_tests.cpp | 2 +- .../vs200x/tests/vs2003_config_tests.cpp | 2 +- .../vs200x/tests/vs2005_config_tests.cpp | 2 +- .../vs200x/tests/vs2008_config_tests.cpp | 2 +- .../vs200x/tests/vs200x_project_tests.cpp | 49 +++- src/action/vs200x/tests/vs200x_xml_tests.cpp | 91 ++------ src/action/vs200x/vs200x.c | 75 ++---- src/action/vs200x/vs200x.h | 8 +- src/action/vs200x/vs200x_config.c | 16 +- src/action/vs200x/vs200x_config.h | 8 +- src/action/vs200x/vs200x_project.c | 214 ++++++++++++------ src/action/vs200x/vs200x_project.h | 1 + src/project/fields.c | 37 ++- src/project/fields.h | 4 + src/project/project.c | 28 ++- src/project/project.h | 3 + src/project/tests/fields_tests.cpp | 7 + src/script/fn_accessor.c | 68 +++++- src/script/fn_dofile.c | 5 + src/script/fn_error.c | 4 + src/script/fn_getcwd.c | 4 + src/script/script.c | 3 +- src/script/tests/fn_accessor_tests.cpp | 53 +++++ src/script/tests/fn_files_tests.cpp | 19 ++ src/session/session.h | 10 + 28 files changed, 624 insertions(+), 231 deletions(-) create mode 100644 src/script/tests/fn_files_tests.cpp diff --git a/src/action/action.c b/src/action/action.c index 609a3e35..f8bdacfa 100644 --- a/src/action/action.c +++ b/src/action/action.c @@ -4,9 +4,11 @@ * \author Copyright (c) 2002-2008 Jason Perkins and the Premake project */ +#include #include "premake.h" #include "action/action.h" - +#include "base/buffers.h" +#include "base/cstr.h" SessionAction Actions[] = { @@ -17,3 +19,97 @@ SessionAction Actions[] = { "vs2008", "Microsoft Visual Studio 2008 (includes Express editions)", vs2008_action }, { 0, 0, 0 } }; + +static int action_source_tree_do(Session sess, Project prj, Stream strm, ActionSourceCallback handler, const char* group); + + +/** + * Walk a list of source files and pass them off, in nesting order, to + * the specified callback. Handles the grouping of related files info + * groups (by directory currently). + * \param sess The current execution session context. + * \param prj The project containing the files to enumerate. + * \param strm The active output stream. + * \param handler The per-file handler function. + * \returns OKAY on success. + */ +int action_source_tree(Session sess, Project prj, Stream strm, ActionSourceCallback handler) +{ + return action_source_tree_do(sess, prj, strm, handler, ""); +} + + +static int action_source_tree_do(Session sess, Project prj, Stream strm, ActionSourceCallback handler, const char* group) +{ + int i, n, group_len; + Strings files; + char* buffer = buffers_next(); + + /* open an enclosing group */ + group_len = strlen(group); + strcpy(buffer, group); + if (cstr_ends_with(buffer, "/")) /* Trim off trailing path separator */ + { + buffer[strlen(buffer)-1] = '\0'; + } + handler(sess, prj, strm, buffer, GroupStart); + + /* scan all files in this group and process any subdirectories (subgroups) */ + files = project_get_files(prj); + n = strings_size(files); + for (i = 0; i < n; ++i) + { + const char* filename = strings_item(files, i); + + /* is this file in the group that I am currently processing? */ + if (cstr_starts_with(filename, group)) + { + /* see if this file contains an additional directory level (a new group) */ + const char* ptr = strchr(filename + group_len, '/'); + if (ptr) + { + int j; + + /* pull out the name of this new group */ + size_t len = ptr - filename + 1; + strncpy(buffer, filename, len); + buffer[len] = '\0'; + + /* have I processed this subdirectory already? See if it appears earlier in the list */ + for (j = 0; j < i; ++j) + { + if (cstr_starts_with(strings_item(files, j), buffer)) + break; + } + + if (i == j) + { + /* a new group, process it now */ + if (action_source_tree_do(sess, prj, strm, handler, buffer) != OKAY) + return !OKAY; + } + } + } + } + + /* now process all files that belong to this current group (and not a subgroup) */ + for (i = 0; i < n; ++i) + { + const char* filename = strings_item(files, i); + if (!strchr(filename + group_len, '/')) + { + if (handler(sess, prj, strm, filename, SourceFile) != OKAY) + return !OKAY; + } + } + + /* close the group */ + strcpy(buffer, group); + if (cstr_ends_with(buffer, "/")) /* Trim off trailing path separator */ + { + buffer[strlen(buffer)-1] = '\0'; + } + handler(sess, prj, strm, buffer, GroupEnd); + return OKAY; +} + diff --git a/src/action/action.h b/src/action/action.h index d517779b..fdeeaf93 100644 --- a/src/action/action.h +++ b/src/action/action.h @@ -8,6 +8,31 @@ #include "session/session.h" + +/** + * State values for the source tree enumeration functions. + */ +enum ActionSourceState +{ + GroupStart, + GroupEnd, + SourceFile +}; + + +/** + * Per-file callback signature for action_source_tree. + * \param sess The current execution state context. + * \param prj The current project; contains the file being enumerated. + * \param strm The active output stream; for writing the file markup. + * \param filename The name of the file to process. + * \param state One of the ActionSourceStates, enabling file grouping. + * \returns OKAY if successful. + */ +typedef int (*ActionSourceCallback)(Session sess, Project prj, Stream strm, const char* filename, int state); + + +/* the list of built-in Premake actions */ extern SessionAction Actions[]; int gmake_action(Session sess); @@ -16,4 +41,9 @@ int vs2003_action(Session sess); int vs2005_action(Session sess); int vs2008_action(Session sess); + +/* support functions */ +int action_source_tree(Session sess, Project prj, Stream strm, ActionSourceCallback handler); + + #endif diff --git a/src/action/tests/action_tests.h b/src/action/tests/action_tests.h index ac8a884d..3e8a1fed 100644 --- a/src/action/tests/action_tests.h +++ b/src/action/tests/action_tests.h @@ -48,4 +48,16 @@ struct FxAction stream_destroy(strm); session_destroy(sess); } + + + void SetField(Project prj, enum ProjectField index, char** values) + { + Strings strs = strings_create(); + for (char** value = values; (*value) != NULL; ++value) + { + strings_add(strs, *value); + } + + project_set_values(prj, index, strs); + } }; diff --git a/src/action/vs200x/tests/vs2002_config_tests.cpp b/src/action/vs200x/tests/vs2002_config_tests.cpp index 9642ad79..8cfafe6a 100644 --- a/src/action/vs200x/tests/vs2002_config_tests.cpp +++ b/src/action/vs200x/tests/vs2002_config_tests.cpp @@ -23,7 +23,7 @@ SUITE(action) { TEST_FIXTURE(Fx2002Config, CharacterSet_Default) { - vs200x_config_character_set(sess); + vs200x_config_character_set(sess, strm); CHECK_EQUAL("\n\t\t\tCharacterSet=\"2\"", buffer); } } diff --git a/src/action/vs200x/tests/vs2003_config_tests.cpp b/src/action/vs200x/tests/vs2003_config_tests.cpp index af37c7e9..2b9b1a1a 100644 --- a/src/action/vs200x/tests/vs2003_config_tests.cpp +++ b/src/action/vs200x/tests/vs2003_config_tests.cpp @@ -23,7 +23,7 @@ SUITE(action) { TEST_FIXTURE(Fx2003Config, CharacterSet_Default) { - vs200x_config_character_set(sess); + vs200x_config_character_set(sess, strm); CHECK_EQUAL("\n\t\t\tCharacterSet=\"2\"", buffer); } } diff --git a/src/action/vs200x/tests/vs2005_config_tests.cpp b/src/action/vs200x/tests/vs2005_config_tests.cpp index e754e596..68f64049 100644 --- a/src/action/vs200x/tests/vs2005_config_tests.cpp +++ b/src/action/vs200x/tests/vs2005_config_tests.cpp @@ -23,7 +23,7 @@ SUITE(action) { TEST_FIXTURE(Fx2005Config, CharacterSet_Default) { - vs200x_config_character_set(sess); + vs200x_config_character_set(sess, strm); CHECK_EQUAL("\n\t\t\tCharacterSet=\"1\"", buffer); } } diff --git a/src/action/vs200x/tests/vs2008_config_tests.cpp b/src/action/vs200x/tests/vs2008_config_tests.cpp index 32d59c60..2a9a7fd3 100644 --- a/src/action/vs200x/tests/vs2008_config_tests.cpp +++ b/src/action/vs200x/tests/vs2008_config_tests.cpp @@ -23,7 +23,7 @@ SUITE(action) { TEST_FIXTURE(Fx2008Config, CharacterSet_Default) { - vs200x_config_character_set(sess); + vs200x_config_character_set(sess, strm); CHECK_EQUAL("\n\t\t\tCharacterSet=\"1\"", buffer); } } diff --git a/src/action/vs200x/tests/vs200x_project_tests.cpp b/src/action/vs200x/tests/vs200x_project_tests.cpp index 3969f180..8cded2ac 100644 --- a/src/action/vs200x/tests/vs200x_project_tests.cpp +++ b/src/action/vs200x/tests/vs200x_project_tests.cpp @@ -222,7 +222,7 @@ SUITE(action) * Files section tests **********************************************************************/ - TEST_FIXTURE(FxVsProject, Vs200x_Files) + TEST_FIXTURE(FxVsProject, Vs200x_Files_OnNoFiles) { vs200x_project_files(sess, prj, strm); CHECK_EQUAL( @@ -231,6 +231,52 @@ SUITE(action) buffer); } + TEST_FIXTURE(FxVsProject, Vs200x_Files_OnSingleCppFile) + { + char* values[] = { "Hello.cpp", 0 }; + SetField(prj, ProjectFiles, values); + vs200x_project_files(sess, prj, strm); + CHECK_EQUAL( + "\t\n" + "\t\t\n" + "\t\t\n" + "\t\n", + buffer); + } + + TEST_FIXTURE(FxVsProject, Vs200x_Files_OnUpperDirectory) + { + char* values[] = { "../../Hello.cpp", 0 }; + SetField(prj, ProjectFiles, values); + vs200x_project_files(sess, prj, strm); + CHECK_EQUAL( + "\t\n" + "\t\t\n" + "\t\t\n" + "\t\n", + buffer); + } + + TEST_FIXTURE(FxVsProject, Vs200x_Files_OnGroupedCppFile) + { + char* values[] = { "Src/Hello.cpp", 0 }; + SetField(prj, ProjectFiles, values); + vs200x_project_files(sess, prj, strm); + CHECK_EQUAL( + "\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\n", + buffer); + } + /********************************************************************** * Globals section tests @@ -245,5 +291,4 @@ SUITE(action) "\n", buffer); } - } diff --git a/src/action/vs200x/tests/vs200x_xml_tests.cpp b/src/action/vs200x/tests/vs200x_xml_tests.cpp index dfb3d5fa..28b81152 100644 --- a/src/action/vs200x/tests/vs200x_xml_tests.cpp +++ b/src/action/vs200x/tests/vs200x_xml_tests.cpp @@ -13,43 +13,6 @@ extern "C" { SUITE(action) { - /********************************************************************** - * Element tests - **********************************************************************/ - - TEST_FIXTURE(FxAction, Element_OnLevel0) - { - session_set_action(sess, "vs2002"); - vs200x_element(sess, 0, "VisualStudioProject"); - CHECK_EQUAL("\n", buffer); - } - - TEST_FIXTURE(FxAction, Element_OnLevel1) - { - session_set_action(sess, "vs2002"); - vs200x_element(sess, 1, "VisualStudioProject"); - CHECK_EQUAL("\t\n", buffer); - } - - /********************************************************************** - * Element start tests - **********************************************************************/ - - TEST_FIXTURE(FxAction, ElementStart_OnLevel0) - { - session_set_action(sess, "vs2002"); - vs200x_element_start(sess, 0, "VisualStudioProject"); - CHECK_EQUAL(""); + vs200x_element_end(sess, strm, 0, "/>"); CHECK_EQUAL("/>\n", buffer); } TEST_FIXTURE(FxAction, ElementEnd_SlashBracket_Vs2003) { session_set_action(sess, "vs2003"); - vs200x_element_end(sess, 0, "/>"); + vs200x_element_end(sess, strm, 0, "/>"); CHECK_EQUAL("/>\n", buffer); } TEST_FIXTURE(FxAction, ElementEnd_SlashBracket_Vs2005) { session_set_action(sess, "vs2005"); - vs200x_element_end(sess, 0, "/>"); + vs200x_element_end(sess, strm, 0, "/>"); CHECK_EQUAL("\n/>\n", buffer); } TEST_FIXTURE(FxAction, ElementEnd_SlashBracket_Vs2008) { session_set_action(sess, "vs2008"); - vs200x_element_end(sess, 0, "/>"); + vs200x_element_end(sess, strm, 0, "/>"); CHECK_EQUAL("\n/>\n", buffer); } TEST_FIXTURE(FxAction, ElementEnd_Bracket_Vs2002) { session_set_action(sess, "vs2002"); - vs200x_element_end(sess, 0, ">"); + vs200x_element_end(sess, strm, 0, ">"); CHECK_EQUAL(">\n", buffer); } TEST_FIXTURE(FxAction, ElementEnd_Bracket_Vs2003) { session_set_action(sess, "vs2003"); - vs200x_element_end(sess, 0, ">"); + vs200x_element_end(sess, strm, 0, ">"); CHECK_EQUAL(">\n", buffer); } TEST_FIXTURE(FxAction, ElementEnd_Bracket_Vs2005) { session_set_action(sess, "vs2005"); - vs200x_element_end(sess, 0, ">"); + vs200x_element_end(sess, strm, 0, ">"); CHECK_EQUAL("\n\t>\n", buffer); } TEST_FIXTURE(FxAction, ElementEnd_Bracket_Vs2008) { session_set_action(sess, "vs2008"); - vs200x_element_end(sess, 0, ">"); + vs200x_element_end(sess, strm, 0, ">"); CHECK_EQUAL("\n\t>\n", buffer); } @@ -118,42 +81,14 @@ SUITE(action) TEST_FIXTURE(FxAction, Attribute_OnLevel0) { session_set_action(sess, "vs2002"); - vs200x_attribute(sess, 0, "ProjectType", "Visual C++"); - CHECK_EQUAL("\n\tProjectType=\"Visual C++\"", buffer); + vs200x_attribute(strm, 0, "ProjectType", "Visual C++"); + CHECK_EQUAL("\nProjectType=\"Visual C++\"", buffer); } - TEST_FIXTURE(FxAction, Attribute_OnLevel1) + TEST_FIXTURE(FxAction, Attribute_OnLevel3) { session_set_action(sess, "vs2002"); - vs200x_attribute(sess, 1, "ProjectType", "Visual C++"); - CHECK_EQUAL("\n\t\tProjectType=\"Visual C++\"", buffer); - } - - TEST_FIXTURE(FxAction, Attribute_BooleanValue_OnVs2002) - { - session_set_action(sess, "vs2002"); - vs200x_attribute(sess, 0, "RuntimeTypeInfo", "true"); - CHECK_EQUAL("\n\tRuntimeTypeInfo=\"TRUE\"", buffer); - } - - TEST_FIXTURE(FxAction, Attribute_BooleanValue_OnVs2003) - { - session_set_action(sess, "vs2003"); - vs200x_attribute(sess, 0, "RuntimeTypeInfo", "true"); - CHECK_EQUAL("\n\tRuntimeTypeInfo=\"TRUE\"", buffer); - } - - TEST_FIXTURE(FxAction, Attribute_BooleanValue_OnVs2005) - { - session_set_action(sess, "vs2005"); - vs200x_attribute(sess, 0, "RuntimeTypeInfo", "true"); - CHECK_EQUAL("\n\tRuntimeTypeInfo=\"true\"", buffer); - } - - TEST_FIXTURE(FxAction, Attribute_BooleanValue_OnVs2008) - { - session_set_action(sess, "vs2008"); - vs200x_attribute(sess, 0, "RuntimeTypeInfo", "true"); - CHECK_EQUAL("\n\tRuntimeTypeInfo=\"true\"", buffer); + vs200x_attribute(strm, 3, "ProjectType", "Visual C++"); + CHECK_EQUAL("\n\t\t\tProjectType=\"Visual C++\"", buffer); } } diff --git a/src/action/vs200x/vs200x.c b/src/action/vs200x/vs200x.c index 86f55bc5..381674b0 100644 --- a/src/action/vs200x/vs200x.c +++ b/src/action/vs200x/vs200x.c @@ -14,33 +14,20 @@ /** * Write an XML attribute, adjusting for the differing Visual Studio formats. - * \param sess The current execution session. - * \param level The XML element nesting level. + * \param strm The output stream, the attribute will be written here. + * \param indent_size How far to indent (with tabs) the attribute. * \param name The attribute name. - * \param value The attribute value. + * \param value The attribute value; may contain printf-style formatting codes. * \returns OKAY if successful. */ -int vs200x_attribute(Session sess, int level, const char* name, const char* value, ...) +int vs200x_attribute(Stream strm, int indent_size, const char* name, const char* value, ...) { - int z; va_list args; - Stream strm = session_get_active_stream(sess); - - if (vs200x_get_target_version(sess) < 2005) - { - if (cstr_eq(value, "true")) - { - value = "TRUE"; - } - else if (cstr_eq(value, "false")) - { - value = "FALSE"; - } - } + int z = OKAY; va_start(args, value); - z = stream_writeline(strm, ""); - z |= stream_write_n(strm, "\t", level + 1); + z |= stream_writeline(strm, ""); + z |= stream_write_n(strm, "\t", indent_size); z |= stream_write(strm, "%s=\"", name); z |= stream_vprintf(strm, value, args); z |= stream_write(strm, "\""); @@ -49,34 +36,17 @@ int vs200x_attribute(Session sess, int level, const char* name, const char* valu } -/** - * Write out an element tag. - * \param sess The current execution session. - * \param level The XML element nesting level. - * \param name The element name. - * \returns OKAY if successful. - */ -int vs200x_element(Session sess, int level, const char* name) -{ - int z; - Stream strm = session_get_active_stream(sess); - z = stream_write_n(strm, "\t", level); - z |= stream_writeline(strm, "<%s>", name); - return z; -} - - /** * Write the ending part of an XML tag, adjust for the differing Visual Studio formats. * \param sess The current execution session. + * \param strm The output stream. * \param level The XML element nesting level. * \param markup The end tag markup. * \returns OKAY if successful. */ -int vs200x_element_end(Session sess, int level, const char* markup) +int vs200x_element_end(Session sess, Stream strm, int level, const char* markup) { int z; - Stream strm = session_get_active_stream(sess); int version = vs200x_get_target_version(sess); if (version >= 2005) { @@ -97,19 +67,13 @@ int vs200x_element_end(Session sess, int level, const char* markup) /** - * Write out the starting part of an XML element tag: " 2003 ? "1" : "2"); + return vs200x_attribute(strm, 3, "CharacterSet", version > 2003 ? "1" : "2"); } -int vs200x_config_detect_64bit_portability(Session sess, Project prj) +int vs200x_config_detect_64bit_portability(Session sess, Stream strm, Project prj) { int version = vs200x_get_target_version(sess); UNUSED(prj); if (version < 2008) { - return vs200x_attribute(sess, 3, "Detect64BitPortabilityProblems", "true"); + return vs200x_attribute(strm, 4, "Detect64BitPortabilityProblems", vs200x_true(sess)); } return OKAY; } -int vs200x_config_runtime_type_info(Session sess, Project prj) +int vs200x_config_runtime_type_info(Session sess, Stream strm, Project prj) { int version = vs200x_get_target_version(sess); UNUSED(prj); if (version < 2005) { - return vs200x_attribute(sess, 3, "RuntimeTypeInfo", "true"); + return vs200x_attribute(strm, 4, "RuntimeTypeInfo", vs200x_true(sess)); } return OKAY; } -int vs200x_config_use_precompiled_header(Session sess, Project prj) +int vs200x_config_use_precompiled_header(Session sess, Stream strm, Project prj) { int version = vs200x_get_target_version(sess); UNUSED(prj); - return vs200x_attribute(sess, 3, "UsePrecompiledHeader", (version > 2003) ? "0" : "2"); + return vs200x_attribute(strm, 4, "UsePrecompiledHeader", (version > 2003) ? "0" : "2"); } diff --git a/src/action/vs200x/vs200x_config.h b/src/action/vs200x/vs200x_config.h index 0b3c87d9..939eae68 100644 --- a/src/action/vs200x/vs200x_config.h +++ b/src/action/vs200x/vs200x_config.h @@ -8,9 +8,9 @@ #include "session/session.h" -int vs200x_config_character_set(Session sess); -int vs200x_config_detect_64bit_portability(Session sess, Project prj); -int vs200x_config_runtime_type_info(Session sess, Project prj); -int vs200x_config_use_precompiled_header(Session sess, Project prj); +int vs200x_config_character_set(Session sess, Stream strm); +int vs200x_config_detect_64bit_portability(Session sess, Stream strm, Project prj); +int vs200x_config_runtime_type_info(Session sess, Stream strm, Project prj); +int vs200x_config_use_precompiled_header(Session sess, Stream strm, Project prj); #endif diff --git a/src/action/vs200x/vs200x_project.c b/src/action/vs200x/vs200x_project.c index 11a8de62..9c73cbf4 100644 --- a/src/action/vs200x/vs200x_project.c +++ b/src/action/vs200x/vs200x_project.c @@ -5,10 +5,14 @@ */ #include +#include #include "premake.h" +#include "action/action.h" #include "vs200x.h" #include "vs200x_project.h" #include "vs200x_config.h" +#include "base/cstr.h" +#include "base/path.h" /** @@ -16,16 +20,15 @@ */ int vs200x_project_config_element(Session sess, Project prj, Stream strm) { - int z; + int z = OKAY; const char* cfg_name = project_get_configuration_filter(prj); - UNUSED(strm); - z = vs200x_element_start(sess, 2, "Configuration"); - z |= vs200x_attribute(sess, 2, "Name", "%s|Win32", cfg_name); - z |= vs200x_attribute(sess, 2, "OutputDirectory", "$(SolutionDir)$(ConfigurationName)"); - z |= vs200x_attribute(sess, 2, "IntermediateDirectory", "$(ConfigurationName)"); - z |= vs200x_attribute(sess, 2, "ConfigurationType", "1"); - z |= vs200x_config_character_set(sess); - z |= vs200x_element_end(sess, 2, ">"); + z |= stream_write(strm, "\t\t"); return z; } @@ -35,9 +38,9 @@ int vs200x_project_config_element(Session sess, Project prj, Stream strm) */ int vs200x_project_config_end(Session sess, Project prj, Stream strm) { + UNUSED(sess); UNUSED(prj); - UNUSED(strm); - return vs200x_element(sess, 2, "/Configuration"); + return stream_writeline(strm, "\t\t"); } @@ -71,8 +74,6 @@ int vs200x_project_element(Session sess, Project prj, Stream strm) const char* prj_name = project_get_name(prj); const char* prj_guid = project_get_guid(prj); - UNUSED(strm); - version = vs200x_get_target_version(sess); switch (version) { @@ -86,21 +87,21 @@ int vs200x_project_element(Session sess, Project prj, Stream strm) prj_ver = "9.00"; break; } - z = vs200x_element_start(sess, 0, "VisualStudioProject"); - z |= vs200x_attribute(sess, 0, "ProjectType", "Visual C++"); - z |= vs200x_attribute(sess, 0, "Version", prj_ver); - z |= vs200x_attribute(sess, 0, "Name", prj_name); - z |= vs200x_attribute(sess, 0, "ProjectGUID", "{%s}", prj_guid); + z = stream_write(strm, " 2003) { - z |= vs200x_attribute(sess, 0, "RootNamespace", prj_name); + z |= vs200x_attribute(strm, 1, "RootNamespace", prj_name); } - z |= vs200x_attribute(sess, 0, "Keyword", "Win32Proj"); + z |= vs200x_attribute(strm, 1, "Keyword", "Win32Proj"); if (version > 2005) { - z |= vs200x_attribute(sess, 0, "TargetFrameworkVersion", "196613"); + z |= vs200x_attribute(strm, 1, "TargetFrameworkVersion", "196613"); } - z |= vs200x_element_end(sess, 0, ">"); + z |= vs200x_element_end(sess, strm, 0, ">"); return z; } @@ -117,16 +118,89 @@ int vs200x_project_encoding(Session sess, Project prj, Stream strm) } +/** + * Write an individual file entry to the project file; callback for action_source_tree(). + * \param sess The current execution session context. + * \param prj The current project; contains the file being enumerated. + * \param strm The active output stream; for writing the file markup. + * \param filename The name of the file to process. + * \param state One of the ActionSourceStates, enabling file grouping. + * \returns OKAY if successful. + */ +int vs200x_project_file(Session sess, Project prj, Stream strm, const char* filename, int state) +{ + const char* name; + const char* ptr; + int depth, z = OKAY; + + /* figure out the grouping depth, skipping over any leading dot directories */ + depth = 2; + + ptr = filename; + while (cstr_starts_with(ptr, "../")) + { + ptr += 3; + } + + ptr = strchr(ptr, '/'); + while (ptr != NULL) + { + depth++; + ptr = strchr(ptr + 1, '/'); + } + + /* group name is just the last bit of the path */ + name = path_filename(filename); + + /* use the Windows path separator */ + filename = path_translate(filename, "\\"); + + switch (state) + { + case GroupStart: + if (strlen(filename) > 0 && !cstr_eq(name, "..")) + { + z |= stream_write_n(strm, "\t", depth); + z |= stream_write(strm, ""); + } + break; + + case GroupEnd: + if (strlen(filename) > 0 && !cstr_eq(name, "..")) + { + z |= stream_write_n(strm, "\t", depth); + z |= stream_writeline(strm, ""); + } + break; + + case SourceFile: + z |= stream_write_n(strm, "\t", depth); + z |= stream_write(strm, ""); + z |= stream_write_n(strm, "\t", depth); + z |= stream_writeline(strm, ""); + break; + } + + UNUSED(prj); + return z; +} + + /** * Write out the [Files] element. */ int vs200x_project_files(Session sess, Project prj, Stream strm) { - int z; - UNUSED(prj); - UNUSED(strm); - z = vs200x_element(sess, 1, "Files"); - z |= vs200x_element(sess, 1, "/Files"); + int z = OKAY; + z |= stream_writeline(strm, "\t"); + z |= action_source_tree(sess, prj, strm, vs200x_project_file); + z |= stream_writeline(strm, "\t"); return z; } @@ -136,12 +210,12 @@ int vs200x_project_files(Session sess, Project prj, Stream strm) */ int vs200x_project_globals(Session sess, Project prj, Stream strm) { - int z; + int z = OKAY; + UNUSED(sess); UNUSED(prj); - UNUSED(strm); - z = vs200x_element(sess, 1, "Globals"); - z |= vs200x_element(sess, 1, "/Globals"); - z |= vs200x_element(sess, 0, "/VisualStudioProject"); + z |= stream_writeline(strm, "\t"); + z |= stream_writeline(strm, "\t"); + z |= stream_writeline(strm, ""); return z; } @@ -151,14 +225,13 @@ int vs200x_project_globals(Session sess, Project prj, Stream strm) */ int vs200x_project_platforms(Session sess, Project prj, Stream strm) { - int z; + int z = OKAY; UNUSED(prj); - UNUSED(strm); - z = vs200x_element(sess, 1, "Platforms"); - z |= vs200x_element_start(sess, 2, "Platform"); - z |= vs200x_attribute(sess, 2, "Name", "Win32"); - z |= vs200x_element_end(sess, 2, "/>"); - z |= vs200x_element(sess, 1, "/Platforms"); + z |= stream_writeline(strm, "\t"); + z |= stream_write(strm, "\t\t"); + z |= stream_writeline(strm, "\t"); return OKAY; } @@ -170,12 +243,11 @@ int vs200x_project_references(Session sess, Project prj, Stream strm) { int z; UNUSED(prj); - UNUSED(strm); - z = vs200x_element(sess, 1, "/Configurations"); + z = stream_writeline(strm, "\t"); if (vs200x_get_target_version(sess) > 2002) { - z |= vs200x_element(sess, 1, "References"); - z |= vs200x_element(sess, 1, "/References"); + z |= stream_writeline(strm, "\t"); + z |= stream_writeline(strm, "\t"); } return z; } @@ -188,15 +260,14 @@ int vs200x_project_tool_files(Session sess, Project prj, Stream strm) { int version, z = OKAY; UNUSED(prj); - UNUSED(strm); version = vs200x_get_target_version(sess); if (version > 2003) { - z |= vs200x_element(sess, 1, "ToolFiles"); - z |= vs200x_element(sess, 1, "/ToolFiles"); + z |= stream_writeline(strm, "\t"); + z |= stream_writeline(strm, "\t"); } - z |= vs200x_element(sess, 1, "Configurations"); + z |= stream_writeline(strm, "\t"); return z; } @@ -208,10 +279,9 @@ static int vs200x_project_vc_empty_tool(Session sess, Project prj, Stream strm, { int z; UNUSED(prj); - UNUSED(strm); - z = vs200x_element_start(sess, 3, "Tool"); - z |= vs200x_attribute(sess, 3, "Name", name); - z |= vs200x_element_end(sess, 3, "/>"); + z = stream_write(strm, "\t\t\t"); return z; } @@ -250,20 +320,19 @@ int vs200x_project_vc_cl_compiler_tool(Session sess, Project prj, Stream strm) { int version, z; UNUSED(prj); - UNUSED(strm); version = vs200x_get_target_version(sess); - z = vs200x_element_start(sess, 3, "Tool"); - z |= vs200x_attribute(sess, 3, "Name", "VCCLCompilerTool"); - z |= vs200x_attribute(sess, 3, "Optimization", "0"); - z |= vs200x_attribute(sess, 3, "MinimalRebuild", "true"); - z |= vs200x_attribute(sess, 3, "BasicRuntimeChecks", "3"); - z |= vs200x_attribute(sess, 3, "RuntimeLibrary", "3"); - z |= vs200x_config_runtime_type_info(sess, prj); - z |= vs200x_config_use_precompiled_header(sess, prj); - z |= vs200x_attribute(sess, 3, "WarningLevel", "3"); - z |= vs200x_config_detect_64bit_portability(sess, prj); - z |= vs200x_attribute(sess, 3, "DebugInformationFormat", "4"); - z |= vs200x_element_end(sess, 3, "/>"); + z = stream_write(strm, "\t\t\t"); return z; } @@ -293,15 +362,14 @@ int vs200x_project_vc_linker_tool(Session sess, Project prj, Stream strm) { int z; UNUSED(prj); - UNUSED(strm); - z = vs200x_element_start(sess, 3, "Tool"); - z |= vs200x_attribute(sess, 3, "Name", "VCLinkerTool"); - z |= vs200x_attribute(sess, 3, "LinkIncremental", "2"); - z |= vs200x_attribute(sess, 3, "GenerateDebugInformation", "true"); - z |= vs200x_attribute(sess, 3, "SubSystem", "1"); - z |= vs200x_attribute(sess, 3, "EntryPointSymbol", "mainCRTStartup"); - z |= vs200x_attribute(sess, 3, "TargetMachine", "1"); - z |= vs200x_element_end(sess, 3, "/>"); + z = stream_write(strm, "\t\t\t"); return z; } diff --git a/src/action/vs200x/vs200x_project.h b/src/action/vs200x/vs200x_project.h index 84d1e7eb..accb053b 100644 --- a/src/action/vs200x/vs200x_project.h +++ b/src/action/vs200x/vs200x_project.h @@ -13,6 +13,7 @@ int vs200x_project_config_end(Session sess, Project prj, Stream strm); int vs200x_project_create(Session sess, Project prj, Stream strm); int vs200x_project_element(Session sess, Project prj, Stream strm); int vs200x_project_encoding(Session sess, Project prj, Stream strm); +int vs200x_project_file(Session sess, Project prj, Stream strm, const char* filename, int state); int vs200x_project_files(Session sess, Project prj, Stream strm); int vs200x_project_globals(Session sess, Project prj, Stream strm); int vs200x_project_platforms(Session sess, Project prj, Stream strm); diff --git a/src/project/fields.c b/src/project/fields.c index e6b9fab3..e8fe3fae 100644 --- a/src/project/fields.c +++ b/src/project/fields.c @@ -75,7 +75,6 @@ void fields_destroy(Fields fields) const char* fields_get_value(Fields fields, int index) { Strings values; - assert(fields); assert(index >= 0 && index < fields->count); @@ -91,6 +90,20 @@ const char* fields_get_value(Fields fields, int index) } +/** + * Retrieve the list of values for a field. + * \param fields The collection of fields. + * \index index The index of fields to query. + * \returns The list of values stored in the field. + */ +Strings fields_get_values(Fields fields, int index) +{ + assert(fields); + assert(index >= 0 && index < fields->count); + return fields->values[index]; +} + + /** * Sets the value of a string (single value) field. * \param fields The collection of fields. @@ -115,3 +128,25 @@ void fields_set_value(Fields fields, int index, const char* value) strings_set(values, 0, value); } } + + +/** + * Sets the list of values associated with a field. The field will subsequently + * "own" the list, and take responsibility to destroying it with the field set. + * \param fields The collection of fields. + * \param index The index of the field to set. + * \param values The list of new values for the field. + */ +void fields_set_values(Fields fields, int index, Strings values) +{ + assert(fields); + assert(index >= 0 && index < fields->count); + assert(values); + + if (fields->values[index] != NULL) + { + strings_destroy(fields->values[index]); + } + + fields->values[index] = values; +} diff --git a/src/project/fields.h b/src/project/fields.h index 89bbd4df..75651df3 100644 --- a/src/project/fields.h +++ b/src/project/fields.h @@ -6,6 +6,8 @@ #if !defined(PREMAKE_FIELDS_H) #define PREMAKE_FIELDS_H +#include "base/strings.h" + /** * Field types. @@ -43,6 +45,8 @@ Fields fields_create(struct FieldInfo* info); void fields_destroy(Fields fields); const char* fields_get_value(Fields fields, int index); +Strings fields_get_values(Fields fields, int index); void fields_set_value(Fields fields, int index, const char* value); +void fields_set_values(Fields fields, int index, Strings values); #endif diff --git a/src/project/project.c b/src/project/project.c index d6806f30..6beec750 100644 --- a/src/project/project.c +++ b/src/project/project.c @@ -17,6 +17,7 @@ struct FieldInfo ProjectFieldInfo[] = { { "basedir", StringField, NULL }, + { "files", ListField, NULL }, { "guid", StringField, guid_is_valid }, { "language", StringField, project_is_valid_language }, { "location", StringField, NULL }, @@ -118,13 +119,22 @@ const char* project_get_filename(Project prj, const char* basename, const char* } +/** + * Retrieve the list of source files associated with a project. + */ +Strings project_get_files(Project prj) +{ + assert(prj); + return fields_get_values(prj->fields, ProjectFiles); +} + + /** * Retrieve the GUID associated with a project. - * \param prj The project to query. - * \returns The GUID associated with the project, or NULL if the GUID has not been set. */ const char* project_get_guid(Project prj) { + assert(prj); return project_get_value(prj, ProjectGuid); } @@ -270,3 +280,17 @@ void project_set_value(Project prj, enum ProjectField field, const char* value) assert(prj); fields_set_value(prj->fields, field, value); } + + +/** + * Sets the list of values associated with a field. The field will subsequently + * "own" the list, and take responsibility to destroying it with the field set. + * \param prj The project object. + * \param index The index of the field to set. + * \param values The list of new values for the field. + */ +void project_set_values(Project prj, enum ProjectField field, Strings values) +{ + assert(prj); + fields_set_values(prj->fields, field, values); +} diff --git a/src/project/project.h b/src/project/project.h index b25425c8..b037fc01 100644 --- a/src/project/project.h +++ b/src/project/project.h @@ -16,6 +16,7 @@ enum ProjectField { ProjectBaseDirectory, + ProjectFiles, ProjectGuid, ProjectLanguage, ProjectLocation, @@ -34,6 +35,7 @@ void project_destroy(Project prj); const char* project_get_base_dir(Project prj); const char* project_get_configuration_filter(Project prj); const char* project_get_filename(Project prj, const char* basename, const char* ext); +Strings project_get_files(Project prj); const char* project_get_guid(Project prj); const char* project_get_language(Project prj); const char* project_get_location(Project prj); @@ -47,6 +49,7 @@ void project_set_language(Project prj, const char* language); void project_set_location(Project prj, const char* location); void project_set_name(Project prj, const char* name); void project_set_value(Project prj, enum ProjectField field, const char* value); +void project_set_values(Project prj, enum ProjectField field, Strings values); int project_tests(void); #endif diff --git a/src/project/tests/fields_tests.cpp b/src/project/tests/fields_tests.cpp index dddcc706..ef05136f 100644 --- a/src/project/tests/fields_tests.cpp +++ b/src/project/tests/fields_tests.cpp @@ -54,4 +54,11 @@ SUITE(fields) const char* result = fields_get_value(fields, TestStringValue); CHECK_EQUAL("String Value", result); } + + TEST_FIXTURE(FxFields, SetValues_CanRoundtrip) + { + Strings values = strings_create(); + fields_set_values(fields, TestListValue, values); + CHECK(values == fields_get_values(fields, TestListValue)); + } } diff --git a/src/script/fn_accessor.c b/src/script/fn_accessor.c index 59f5c024..01554f0a 100644 --- a/src/script/fn_accessor.c +++ b/src/script/fn_accessor.c @@ -10,11 +10,13 @@ #include "base/error.h" +static int fn_accessor(lua_State* L); static int fn_accessor_object_has_field(struct FieldInfo* fields, const char* field_name); static int fn_accessor_register(lua_State* L, struct FieldInfo* fields); static int fn_accessor_register_field(lua_State* L, struct FieldInfo* field); static int fn_accessor_set_string_value(lua_State* L, struct FieldInfo* field); -static int fn_accessor(lua_State* L); +static int fn_accessor_set_list_value(lua_State* L, struct FieldInfo* field); +static void fn_accessor_append_values(lua_State* L, int destIndex, int srcIndex); /** @@ -119,7 +121,14 @@ static int fn_accessor(lua_State* L) /* if a value is provided, set the field */ if (lua_gettop(L) > 1) { - fn_accessor_set_string_value(L, field); + if (field->kind == StringField) + { + fn_accessor_set_string_value(L, field); + } + else + { + fn_accessor_set_list_value(L, field); + } } /* return the current value of the field */ @@ -129,7 +138,7 @@ static int fn_accessor(lua_State* L) /** - * Sets a string field to the value on the top of the Lua stack. + * Sets a string field to the value on the bottom of the Lua stack. * \returns OKAY if successful. */ static int fn_accessor_set_string_value(lua_State* L, struct FieldInfo* field) @@ -157,3 +166,56 @@ static int fn_accessor_set_string_value(lua_State* L, struct FieldInfo* field) lua_setfield(L, -2, field->name); return OKAY; } + + +/** + * Appends the value or list at the bottom of the Lua stack to the specified list field. + * \returns OKAY if successful. + */ +static int fn_accessor_set_list_value(lua_State* L, struct FieldInfo* field) +{ + /* get the current value of the field */ + lua_getfield(L, -1, field->name); + + /* if the value passed in is a table, append all of its contents to the current + * field value. If the value passed in is a single string, add it at the end */ + if (lua_istable(L, 1)) + { + fn_accessor_append_values(L, lua_gettop(L), 1); + } + else + { + lua_pushvalue(L, 1); + lua_rawseti(L, -2, luaL_getn(L, -2) + 1); + } + + /* remove the field value from the stack */ + lua_pop(L, 1); + return OKAY; +} + + +/** + * Copy values from one table to the end of another table. + * \param destIndex The absolute stack index of the destination table. + * \param srcIndex The absolute stack index of the source table. + */ +static void fn_accessor_append_values(lua_State* L, int destIndex, int srcIndex) +{ + int i; + int src_n = luaL_getn(L, srcIndex); + int dst_n = luaL_getn(L, destIndex); + for (i = 1; i <= src_n; ++i) + { + lua_rawgeti(L, srcIndex, i); + if (lua_istable(L, -1)) + { + fn_accessor_append_values(L, destIndex, lua_gettop(L)); + dst_n = luaL_getn(L, destIndex); + } + else + { + lua_rawseti(L, destIndex, ++dst_n); + } + } +} diff --git a/src/script/fn_dofile.c b/src/script/fn_dofile.c index fc4bb607..f52822c1 100644 --- a/src/script/fn_dofile.c +++ b/src/script/fn_dofile.c @@ -11,6 +11,11 @@ #include "base/string.h" +/** + * Replacement implementation for Lua's dofile() function; manages Premake specific + * features like the _FILE variable, and makes sure the directory containing the + * running script is kept current. + */ int fn_dofile(lua_State* L) { const char *filename; diff --git a/src/script/fn_error.c b/src/script/fn_error.c index 47b36833..a63d5b90 100644 --- a/src/script/fn_error.c +++ b/src/script/fn_error.c @@ -9,6 +9,10 @@ #include "base/error.h" +/** + * Handler for errors reported out the script; copies the error message to + * Premake's global error state. + */ int fn_error(lua_State* L) { const char* message = lua_tostring(L, 1); diff --git a/src/script/fn_getcwd.c b/src/script/fn_getcwd.c index 3845260e..9d2fa677 100644 --- a/src/script/fn_getcwd.c +++ b/src/script/fn_getcwd.c @@ -8,6 +8,10 @@ #include "script_internal.h" #include "base/dir.h" + +/** + * Implementation of os.getcwd(): returns the current working directory. + */ int fn_getcwd(lua_State* L) { const char* cwd = dir_get_current(); diff --git a/src/script/script.c b/src/script/script.c index 87dcfed6..65d52d3c 100644 --- a/src/script/script.c +++ b/src/script/script.c @@ -242,7 +242,8 @@ void script_set_action(Script script, const char* action) /** * Copy project information out of the scripting environment and into C objects that * can be more easily manipulated by the action code. - * \param slns An array to hold the list of unloaded solutions. + * \param script The project scripting engine instance. + * \param slns An array to hold the list of unloaded solutions. * \returns OKAY if successful. */ int script_unload(Script script, Array slns) diff --git a/src/script/tests/fn_accessor_tests.cpp b/src/script/tests/fn_accessor_tests.cpp index 27f06be3..9143c1e1 100644 --- a/src/script/tests/fn_accessor_tests.cpp +++ b/src/script/tests/fn_accessor_tests.cpp @@ -64,4 +64,57 @@ SUITE(script) CHECK_EQUAL("MyLocation", result); } + + /************************************************************************** + * List field tests + **************************************************************************/ + + TEST_FIXTURE(FxAccessor, Accessor_ReturnsEmptyTable_OnEmptyListValue) + { + const char* result = script_run_string(script, + "return (#files() == 0)"); + CHECK_EQUAL("true", result); + } + + TEST_FIXTURE(FxAccessor, Accessor_Appends_OnStringValue) + { + const char* result = script_run_string(script, + "files { 'Hello.c' };" + "return (prj.files[1] == 'Hello.c')" ); + CHECK_EQUAL("true", result); + } + + TEST_FIXTURE(FxAccessor, Accessor_Appends_OnListValue) + { + const char* result = script_run_string(script, + "files { 'Hello.c', 'Goodbye.c' };" + "return (prj.files[1] == 'Hello.c' and prj.files[2] == 'Goodbye.c')" ); + CHECK_EQUAL("true", result); + } + + TEST_FIXTURE(FxAccessor, Accessor_Appends_OnTwoCalls) + { + const char* result = script_run_string(script, + "files { 'Hello.c' };" + "files { 'Goodbye.c' };" + "return (prj.files[1] == 'Hello.c' and prj.files[2] == 'Goodbye.c')" ); + CHECK_EQUAL("true", result); + } + + TEST_FIXTURE(FxAccessor, Accessor_ReturnsList_OnNoArgs) + { + const char* result = script_run_string(script, + "files { 'Hello.c', 'Goodbye.c' };" + "lst = files();" + "return (lst[1] == 'Hello.c' and lst[2] == 'Goodbye.c')" ); + CHECK_EQUAL("true", result); + } + + TEST_FIXTURE(FxAccessor, Accessor_FlattensTables_OnNestedLists) + { + const char* result = script_run_string(script, + "files { {'Hello.c'}, {'Goodbye.c'} };" + "return (prj.files[1] == 'Hello.c' and prj.files[2] == 'Goodbye.c')" ); + CHECK_EQUAL("true", result); + } } diff --git a/src/script/tests/fn_files_tests.cpp b/src/script/tests/fn_files_tests.cpp new file mode 100644 index 00000000..04607b23 --- /dev/null +++ b/src/script/tests/fn_files_tests.cpp @@ -0,0 +1,19 @@ +/** + * \file fn_files_tests.cpp + * \brief Automated tests for the files() function. + * \author Copyright (c) 2007-2008 Jason Perkins and the Premake project + */ + +#include "premake.h" +#include "script_tests.h" + + +SUITE(script) +{ + TEST_FIXTURE(FxAccessor, Files_Exists_OnStartup) + { + const char* result = script_run_string(script, + "return (files ~= nil)"); + CHECK_EQUAL("true", result); + } +} diff --git a/src/session/session.h b/src/session/session.h index 12943801..32a1fb1b 100644 --- a/src/session/session.h +++ b/src/session/session.h @@ -2,6 +2,14 @@ * \file session.h * \brief Context for a program execution session. * \author Copyright (c) 2008 Jason Perkins and the Premake project + * + * \defgroup session Session + * + * Premake is essentially a long chain of sequential actions; the Session object + * tracks the application state through this chain, and provides the context + * necessary for actions to do their work. It's a glorified global, essentially. + * + * @{ */ #if !defined(PREMAKE_SESSION_H) #define PREMAKE_SESSION_H @@ -74,3 +82,5 @@ int session_unload(Session sess); int session_validate(Session sess); #endif +/** @} */ +