Added project.language and session validation

This commit is contained in:
starkos 2008-04-24 20:20:12 +00:00
parent cb88b243b8
commit 3621f5fbe9
41 changed files with 823 additions and 272 deletions

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include "premake.h"
#include "action/action.h"
#include "make.h"
#include "make_solution.h"
@ -37,6 +38,12 @@ static SessionProjectCallback ProjectCallbacks[] =
*/
int gmake_action(Session sess)
{
/* make sure I can support all of the features used in the session */
if (make_validate_session(sess) != OKAY)
{
return !OKAY;
}
stream_writeline(Console, "Generating project files for GNU make...");
return session_enumerate_objects(sess, SolutionCallbacks, ProjectCallbacks);
}

View File

@ -7,8 +7,9 @@
#include <assert.h>
#include <stdlib.h>
#include "premake.h"
#include "action/make/make.h"
#include "make.h"
#include "base/cstr.h"
#include "base/error.h"
/**
@ -113,3 +114,40 @@ const char* make_get_solution_makefile(Session sess, Solution sln)
/* all good */
return my_path;
}
/**
* Make sure all of the features described in the sesson are supported
* by the Make-based actions.
* \param sess The session to validate.
* \returns OKAY if the session can be supported.
*/
int make_validate_session(Session sess)
{
int si, sn;
assert(sess);
sn = session_num_solutions(sess);
for (si = 0; si < sn; ++si)
{
int pi, pn;
Solution sln = session_get_solution(sess, si);
pn = solution_num_projects(sln);
for (pi = 0; pi < pn; ++pi)
{
const char* value;
Project prj = solution_get_project(sln, pi);
/* check for a recognized language */
value = project_get_language(prj);
if (!cstr_eq(value, "c") && !cstr_eq(value, "c++"))
{
error_set("%s is not currently supported for Make", value);
return !OKAY;
}
}
}
return OKAY;
}

View File

@ -10,5 +10,6 @@
const char* make_get_project_makefile(Session sess, Project prj);
const char* make_get_solution_makefile(Session sess, Solution sln);
int make_validate_session(Session sess);
#endif

View File

@ -56,6 +56,10 @@ struct FxMake
SUITE(action)
{
/**********************************************************************
* Makefile naming tests
**********************************************************************/
TEST_FIXTURE(FxMake, GetSolutionMakefile_ReturnsMakefile_OnUniqueLocation)
{
solution_set_location(sln1, "MySolution");
@ -90,4 +94,16 @@ SUITE(action)
const char* result = make_get_project_makefile(sess, prj1);
CHECK_EQUAL("./MyProject/MyProject1.make", result);
}
/**********************************************************************
* Session validation tests
**********************************************************************/
TEST_FIXTURE(FxMake, MakeValidation_ReturnsNotOkay_OnUnknownLanguage)
{
project_set_language(prj1, "nonesuch");
int result = make_validate_session(sess);
CHECK(result != OKAY);
}
}

View File

@ -38,6 +38,7 @@ struct FxAction
project_set_base_dir(prj, "/Root");
project_set_location(prj, "ProjectFolder");
project_set_guid(prj, "AE2461B7-236F-4278-81D3-F0D476F9A4C0");
project_set_language(prj, "c++");
}
~FxAction()

View File

@ -37,4 +37,51 @@ SUITE(action)
int result = vs200x_get_target_version(sess);
CHECK(result == 2005);
}
/**********************************************************************
* Language GUID tests
**********************************************************************/
TEST(ToolGuid_ReturnsCorrectGUID_OnC)
{
const char* result = vs200x_tool_guid("c");
CHECK_EQUAL("8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942", result);
}
TEST(ToolGuid_ReturnsCorrectGUID_OnCpp)
{
const char* result = vs200x_tool_guid("c++");
CHECK_EQUAL("8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942", result);
}
TEST(ToolGuid_ReturnsCorrectGUID_OnCSharp)
{
const char* result = vs200x_tool_guid("c#");
CHECK_EQUAL("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC", result);
}
/**********************************************************************
* Language file extensions
**********************************************************************/
TEST_FIXTURE(FxAction, ProjectExtension_IsVcproj_ForC)
{
project_set_language(prj, "c");
const char* result = vs200x_project_file_extension(prj);
CHECK_EQUAL(".vcproj", result);
}
/**********************************************************************
* Session validation
**********************************************************************/
TEST_FIXTURE(FxAction, Vs200xValidation_ReturnsNotOkay_OnUnknownLanguage)
{
project_set_language(prj, "nonesuch");
int result = vs200x_validate_session(sess);
CHECK(result != OKAY);
}
}

View File

@ -7,7 +7,9 @@
#include <stdlib.h>
#include "premake.h"
#include "action/action.h"
#include "vs200x.h"
#include "vs200x_solution.h"
#include "vs200x_project.h"
/** The VS2002 solution writing process, for session_enumerate_objects() */
@ -26,6 +28,7 @@ static SessionSolutionCallback SolutionCallbacks[] =
/** The VS2002 project writing process, for session_enumerate_objects() */
static SessionProjectCallback ProjectCallbacks[] =
{
vs2002_project_create,
NULL
};
@ -37,6 +40,12 @@ static SessionProjectCallback ProjectCallbacks[] =
*/
int vs2002_action(Session sess)
{
/* make sure I can support all of the features used in the session */
if (vs200x_validate_session(sess) != OKAY)
{
return !OKAY;
}
stream_writeline(Console, "Generating project files for Visual Studio 2002...");
return session_enumerate_objects(sess, SolutionCallbacks, ProjectCallbacks);
}

View File

@ -0,0 +1,34 @@
/**
* \file vs2002_project.c
* \brief Visual Studio 2002 project generation functions.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#include <stdlib.h>
#include "premake.h"
#include "vs200x.h"
#include "vs200x_project.h"
/**
* Create a new output stream for a project, and make it active for subsequent writes.
* \param sess The execution session context.
* \param prj The current project.
* \param strm The currently active stream; set with session_set_active_stream().
* \returns OKAY if successful.
*/
int vs2002_project_create(Session sess, Project prj, Stream strm)
{
/* create the project file */
const char* extension = vs200x_project_file_extension(prj);
const char* filename = project_get_filename(prj, NULL, extension);
strm = stream_create_file(filename);
if (!strm)
{
return !OKAY;
}
/* make the stream active for the functions that come after */
session_set_active_stream(sess, strm);
return OKAY;
}

View File

@ -40,6 +40,29 @@ int vs2002_solution_configuration(Session sess, Solution sln, Stream strm)
}
/**
* Create a new output stream for a solution, and make it active for subsequent writes.
* \param sess The execution session context.
* \param sln The current solution.
* \param strm The currently active stream; set with session_set_active_stream().
* \returns OKAY if successful.
*/
int vs2002_solution_create(Session sess, Solution sln, Stream strm)
{
/* create the solution file */
const char* filename = solution_get_filename(sln, NULL, ".sln");
strm = stream_create_file(filename);
if (!strm)
{
return !OKAY;
}
/* make the stream active for the functions that come after */
session_set_active_stream(sess, strm);
return OKAY;
}
/**
* Create the Visual Studio 2002 project dependencies block.
* \param sess The execution session context.
@ -79,29 +102,6 @@ int vs2002_solution_extensibility(Session sess, Solution sln, Stream strm)
}
/**
* Create a new output stream for a solution, and make it active for subsequent writes.
* \param sess The execution session context.
* \param sln The current solution.
* \param strm The currently active stream; set with session_set_active_stream().
* \returns OKAY if successful.
*/
int vs2002_solution_create(Session sess, Solution sln, Stream strm)
{
/* create the solution file */
const char* filename = solution_get_filename(sln, NULL, ".sln");
strm = stream_create_file(filename);
if (!strm)
{
return !OKAY;
}
/* make the stream active for the functions that come after */
session_set_active_stream(sess, strm);
return OKAY;
}
/**
* Write out the Visual Studio 2002 project configurations block.
* \param sess The execution session context.
@ -158,7 +158,7 @@ int vs2002_solution_projects(Session sess, Solution sln, Stream strm)
const char* prj_name = project_get_name(prj);
const char* prj_id = project_get_guid(prj);
const char* prj_lang = project_get_language(prj);
const char* prj_ext = vs200x_project_extension(prj);
const char* prj_ext = vs200x_project_file_extension(prj);
const char* prj_file = project_get_filename(prj, prj_name, prj_ext);
const char* tool_id = vs200x_tool_guid(prj_lang);

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include "premake.h"
#include "action/action.h"
#include "vs200x.h"
#include "vs200x_solution.h"
@ -36,6 +37,12 @@ static SessionProjectCallback ProjectCallbacks[] =
*/
int vs2003_action(Session sess)
{
/* make sure I can support all of the features used in the session */
if (vs200x_validate_session(sess) != OKAY)
{
return !OKAY;
}
stream_writeline(Console, "Generating project files for Visual Studio 2003...");
return session_enumerate_objects(sess, SolutionCallbacks, ProjectCallbacks);
}

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include "premake.h"
#include "action/action.h"
#include "vs200x.h"
#include "vs200x_solution.h"
@ -36,6 +37,12 @@ static SessionProjectCallback ProjectCallbacks[] =
*/
int vs2005_action(Session sess)
{
/* make sure I can support all of the features used in the session */
if (vs200x_validate_session(sess) != OKAY)
{
return !OKAY;
}
stream_writeline(Console, "Generating project files for Visual Studio 2005...");
return session_enumerate_objects(sess, SolutionCallbacks, ProjectCallbacks);
}

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include "premake.h"
#include "action/action.h"
#include "vs200x.h"
#include "vs200x_solution.h"
@ -36,6 +37,12 @@ static SessionProjectCallback ProjectCallbacks[] =
*/
int vs2008_action(Session sess)
{
/* make sure I can support all of the features used in the session */
if (vs200x_validate_session(sess) != OKAY)
{
return !OKAY;
}
stream_writeline(Console, "Generating project files for Visual Studio 2008...");
return session_enumerate_objects(sess, SolutionCallbacks, ProjectCallbacks);
}

View File

@ -40,6 +40,26 @@ int vs200x_get_target_version(Session sess)
}
/**
* Return the appropriate file extension for a particular project.
* \param prj The project object.
* \returns The appropriate project file extension, based on the project settings.
*/
const char* vs200x_project_file_extension(Project prj)
{
const char* language = project_get_language(prj);
if (cstr_eq(language, "c") || cstr_eq(language, "c++"))
{
return ".vcproj";
}
else
{
error_set("unsupported language '%s'", language);
return NULL;
}
}
/**
* Returns the Visual Studio GUID for a particular project type.
* \param language The programming language used in the project.
@ -61,3 +81,40 @@ const char* vs200x_tool_guid(const char* language)
return NULL;
}
}
/**
* Make sure all of the features described in the sesson are supported
* by the Visual Studio actions.
* \param sess The session to validate.
* \returns OKAY if the session can be supported.
*/
int vs200x_validate_session(Session sess)
{
int si, sn;
assert(sess);
sn = session_num_solutions(sess);
for (si = 0; si < sn; ++si)
{
int pi, pn;
Solution sln = session_get_solution(sess, si);
pn = solution_num_projects(sln);
for (pi = 0; pi < pn; ++pi)
{
const char* value;
Project prj = solution_get_project(sln, pi);
/* check for a recognized language */
value = project_get_language(prj);
if (!cstr_eq(value, "c") && !cstr_eq(value, "c++"))
{
error_set("%s is not currently supported for Visual Studio", value);
return !OKAY;
}
}
}
return OKAY;
}

View File

@ -9,8 +9,9 @@
#include "engine/session.h"
int vs200x_get_target_version(Session sess);
const char* vs200x_project_file_extension(Project prj);
const char* vs200x_tool_guid(const char* language);
int vs200x_validate_session(Session sess);
#endif

View File

@ -1,20 +0,0 @@
/**
* \file vs200x_project.c
* \brief Visual Studio 200x project generation functions.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "vs200x_project.h"
/**
* Returns the correct project file extension for a particular project definition.
* \param prj The project to be identified.
* \returns The project file extension for the given project.
*/
const char* vs200x_project_extension(Project prj)
{
prj = 0;
return ".vcproj";
}

View File

@ -8,6 +8,6 @@
#include "engine/session.h"
const char* vs200x_project_extension(Project prj);
int vs2002_project_create(Session sess, Project prj, Stream strm);
#endif

View File

@ -45,3 +45,19 @@ int cstr_eq(const char* str, const char* expected)
}
return 0;
}
/**
* Determines if the given C string starts with a particular sequence.
* \param str The string to test.
* \param expected The sequence for which to look.
* \returns True if the string starts with the sequence, false otherwise.
*/
int cstr_starts_with(const char* str, const char* expected)
{
if (str != NULL && expected != NULL)
{
return (strncmp(str, expected, strlen(expected)) == 0);
}
return 0;
}

View File

@ -8,5 +8,6 @@
int cstr_ends_with(const char* str, const char* expected);
int cstr_eq(const char* str, const char* expected);
int cstr_starts_with(const char* str, const char* expected);
#endif

View File

@ -65,4 +65,34 @@ SUITE(cstr)
{
CHECK(!cstr_eq("something", NULL));
}
/**************************************************************************
* cstr_starts_with() tests
**************************************************************************/
TEST(CStrStartsWith_ReturnsTrue_OnMatch)
{
CHECK(cstr_starts_with("Abcdef", "Abc"));
}
TEST(CStrStartsWith_ReturnsFalse_OnMismatch)
{
CHECK(!cstr_starts_with("Abcdef", "ghi"));
}
TEST(CStrStartsWith_ReturnsFalse_OnLongerNeedle)
{
CHECK(!cstr_starts_with("Abc", "Abcdef"));
}
TEST(CStrStartsWith_ReturnsFalse_OnNullHaystack)
{
CHECK(!cstr_starts_with(NULL, "ghi"));
}
TEST(CStrStartsWith_ReturnsFalse_OnNullNeedle)
{
CHECK(!cstr_starts_with("Abc", NULL));
}
}

164
src/engine/accessor.c Normal file
View File

@ -0,0 +1,164 @@
/**
* \file accessor.c
* \brief A generic getter/setter for project fields.
* \author Copyright (c) 2007-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "internals.h"
#include "base/cstr.h"
#include "base/error.h"
static int accessor_object_has_field(struct FieldInfo* fields, const char* field_name);
static int accessor_register(lua_State* L, struct FieldInfo* fields);
static int accessor_register_field(lua_State* L, struct FieldInfo* field);
static int accessor_set_string_value(lua_State* L, struct FieldInfo* field);
static int fn_accessor(lua_State* L);
/**
* Register all of the accessors listed in the project object field information.
* \param L The Lua scripting state.
* \returns OKAY if successful.
*/
int accessor_register_all(lua_State* L)
{
int z = OKAY;
if (z == OKAY) z = accessor_register(L, SolutionFieldInfo);
if (z == OKAY) z = accessor_register(L, ProjectFieldInfo);
return z;
}
/**
* Register accessor functions for a set of fields.
* \param L The Lua scripting state.
* \param fields The list of fields to register.
* \returns OKAY if successful.
*/
int accessor_register(lua_State* L, struct FieldInfo* fields)
{
int i, z = OKAY;
for (i = 0; z == OKAY && fields[i].name != NULL; ++i)
{
z = accessor_register_field(L, &fields[i]);
}
return z;
}
/**
* Register a single accessor function.
* \param L The Lua scripting state.
* \param field The field to register.
* \returns OKAY if successful.
*/
int accessor_register_field(lua_State* L, struct FieldInfo* field)
{
int container_type, z;
/* has this accessor already been registered? If so, skip it now */
lua_getglobal(L, field->name);
z = lua_isnil(L, -1);
lua_pop(L, 1);
if (!z) return OKAY;
/* figure out what object types this accessor applies to */
container_type = 0;
if (accessor_object_has_field(SolutionFieldInfo, field->name)) container_type |= SolutionObject;
if (accessor_object_has_field(ProjectFieldInfo, field->name)) container_type |= ProjectObject;
/* register the accessor function */
lua_pushnumber(L, container_type);
lua_pushlightuserdata(L, field);
lua_pushcclosure(L, fn_accessor, 2);
lua_setglobal(L, field->name);
return OKAY;
}
/**
* Determine if a field list contains a field with a particular name.
* \param fields The list of fields to check.
* \param field_name The field to look for.
* \returns True if the field is contained by the list.
*/
int accessor_object_has_field(struct FieldInfo* fields, const char* field_name)
{
int i;
for (i = 0; fields[i].name != NULL; ++i)
{
if (cstr_eq(fields[i].name, field_name))
return 1;
}
return 0;
}
/**
* Sets a string field, using the value on the stack.
* \param L The Lua state.
* \param field The field to set.
* \returns OKAY if successful.
*/
int accessor_set_string_value(lua_State* L, struct FieldInfo* field)
{
/* can't set lists to simple fields */
if (lua_istable(L, 1))
{
luaL_error(L, "the field '%s' does not support lists of values", field->name);
return !OKAY;
}
/* if a validator function is present, call it */
if (field->validator != NULL)
{
const char* value = luaL_checkstring(L, 1);
if (!field->validator(value))
{
luaL_error(L, "invalid value '%s'", value);
return !OKAY;
}
}
/* set the field */
lua_pushvalue(L, 1);
lua_setfield(L, -2, field->name);
return OKAY;
}
/**
* The accessor function; this is what gets called by Lua when an accessor
* function is called in a script.
* \param L The Lua state.
* \returns The current value of the field.
*/
int fn_accessor(lua_State* L)
{
struct FieldInfo* field;
int container_type;
/* get the required container object */
container_type = lua_tointeger(L, lua_upvalueindex(1));
if (!engine_get_active_object(L, container_type, REQUIRED))
{
return 0;
}
/* get field information */
field = (struct FieldInfo*)lua_touserdata(L, lua_upvalueindex(2));
/* if a value is provided, set the field */
if (lua_gettop(L) > 1)
{
accessor_set_string_value(L, field);
}
/* return the current value of the field */
lua_getfield(L, -1, field->name);
return 1;
}

View File

@ -1,39 +0,0 @@
/**
* \file fn_guid.c
* \brief Specify a project GUID.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "internals.h"
#include "base/guid.h"
/**
* Specify a project GUID, which is used by the Visual Studio actions to
* identify the project in the solution.
*/
int fn_guid(lua_State* L)
{
const char* guid = luaL_optstring(L, 1, NULL);
/* retrieve the project being set */
if (!engine_get_active_object(L, ProjectObject, REQUIRED))
return 0;
/* if a value is provided, set it */
if (guid != NULL)
{
if (!guid_is_valid(guid))
{
luaL_error(L, "invalid GUID");
return 0;
}
lua_pushvalue(L, 1);
lua_setfield(L, -2, ProjectFieldInfo[ProjectGuid].name);
}
/* return the current value */
lua_getfield(L, -1, ProjectFieldInfo[ProjectGuid].name);
return 1;
}

View File

@ -48,6 +48,10 @@ void engine_configure_project_object(lua_State* L, struct FieldInfo* fiel
lua_State* session_get_lua_state(Session sess);
/* Generic project object field getter/setter API */
int accessor_register_all(lua_State* L);
/* Project object unloading API. The unload functions "interface" provides an
* opportunity to mock the actual implementation for automated testing */
struct UnloadFuncs
@ -65,11 +69,9 @@ int unload_project(Session sess, lua_State* L, Project prj);
int fn_dofile(lua_State* L);
int fn_error(lua_State* L);
int fn_getcwd(lua_State* L);
int fn_guid(lua_State* L);
int fn_include(lua_State* L);
int fn_project(lua_State* L);
int fn_solution(lua_State* L);
#endif

View File

@ -11,6 +11,7 @@
#include <string.h>
#include "premake.h"
#include "base/array.h"
#include "base/cstr.h"
#include "base/error.h"
#include "internals.h"
@ -18,7 +19,6 @@
/** Functions to add to the global namespace */
static const luaL_Reg funcs[] = {
{ "dofile", fn_dofile },
{ "guid", fn_guid },
{ "include", fn_include },
{ "project", fn_project },
{ "solution", fn_solution },
@ -59,6 +59,9 @@ Session session_create()
/* install all the standard libraries */
luaL_openlibs(L);
/* register the project object accessor functions */
accessor_register_all(L);
/* register the Premake non-configuration related functions */
luaL_register(L, "_G", funcs);
luaL_register(L, "os", os_funcs);
@ -298,9 +301,17 @@ const char* session_run_file(Session sess, const char* filename)
*/
const char* session_run_string(Session sess, const char* script)
{
const char* result;
assert(sess);
assert(script);
return session_run(sess->L, script, 0);
result = session_run(sess->L, script, 0);
if (cstr_starts_with(result, "[string "))
{
result = strstr(result, ":1:") + 4;
}
return result;
}
@ -340,7 +351,7 @@ void session_set_active_stream(Session sess, Stream strm)
/**
* Copy project information out of the scripting environment and into C objects that
* can be more easily manipulated by the action code.
* \param sess The session object which contains the scripted project objects.
* \param sess The session object which contains the scripted project objects.
* \returns OKAY if successful.
*/
int session_unload(Session sess)
@ -355,3 +366,48 @@ int session_unload(Session sess)
result = unload_all(sess, sess->L, &funcs);
return result;
}
/**
* Make sure that all required objects and values have been defined by the
* project script.
* \param sess The session to validate.
* \returns OKAY if the session is valid.
*/
int session_validate(Session sess)
{
int si, sn;
assert(sess);
sn = session_num_solutions(sess);
for (si = 0; si < sn; ++si)
{
int pi, pn;
Solution sln = session_get_solution(sess, si);
/* every solution must have at least one project */
pn = solution_num_projects(sln);
if (pn == 0)
{
error_set("no projects defined for solution '%s'", solution_get_name(sln));
return !OKAY;
}
for (pi = 0; pi < pn; ++pi)
{
const char* value;
Project prj = solution_get_project(sln, pi);
/* every project must have a language defined */
value = project_get_language(prj);
if (value == NULL)
{
error_set("no language defined for project '%s'", project_get_name(prj));
return !OKAY;
}
}
}
return OKAY;
}

View File

@ -68,5 +68,6 @@ const char* session_run_string(Session sess, const char* script);
void session_set_action(Session sess, const char* action);
void session_set_active_stream(Session sess, Stream strm);
int session_unload(Session sess);
int session_validate(Session sess);
#endif

View File

@ -0,0 +1,67 @@
/**
* \file accessor_tests.cpp
* \brief Automated tests for the generic value getter/setter function.
* \author Copyright (c) 2007-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "accessor_tests.h"
SUITE(engine)
{
/**************************************************************************
* Initial state tests
**************************************************************************/
TEST_FIXTURE(FxAccessor, Accessor_FunctionExists_OnStartup)
{
const char* result = session_run_string(sess,
"return (location ~= nil)");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FxAccessor, Accessor_RaisesError_OnNoActiveObject)
{
Session sess = session_create();
const char* result = session_run_string(sess, "location()");
CHECK_EQUAL("no active solution or project", result);
session_destroy(sess);
}
/**************************************************************************
* String field tests
**************************************************************************/
TEST_FIXTURE(FxAccessor, Accessor_ReturnsNil_OnEmptyStringValue)
{
const char* result = session_run_string(sess,
"return (location() == nil)");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FxAccessor, Accessor_RaisesError_OnListValueAndStringField)
{
const char* result = session_run_string(sess,
"location {}");
CHECK_EQUAL("the field 'location' does not support lists of values", result);
}
TEST_FIXTURE(FxAccessor, Accessor_SetsField_OnStringField)
{
const char* result = session_run_string(sess,
"location 'MyLocation';"
"return prj.location" );
CHECK_EQUAL("MyLocation", result);
}
TEST_FIXTURE(FxAccessor, Accessor_GetsField_OnStringField)
{
const char* result = session_run_string(sess,
"prj.location = 'MyLocation';"
"return location()" );
CHECK_EQUAL("MyLocation", result);
}
}

View File

@ -0,0 +1,31 @@
/**
* \file accessor_tests.h
* \brief Common fixture for accessor function tests.
* \author Copyright (c) 2008 Jason Perkins and the Premake project
*/
#include "testing/testing.h"
extern "C" {
#include "engine/session.h"
#include "base/error.h"
}
struct FxAccessor
{
Session sess;
FxAccessor()
{
sess = session_create();
session_run_string(sess,
"sln = solution 'MySolution';"
"prj = project 'MyProject';");
}
~FxAccessor()
{
session_destroy(sess);
error_clear();
}
};

View File

@ -1,88 +1,37 @@
/**
* \file fn_guid_tests.cpp
* \brief Automated tests for the guid() function.
* \author Copyright (c) 2008 Jason Perkins and the Premake project
* \author Copyright (c) 2007-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "testing/testing.h"
#include "accessor_tests.h"
extern "C" {
#include "engine/session.h"
#include "base/guid.h"
#include "base/error.h"
}
struct FnGuid
{
Session sess;
FnGuid()
{
sess = session_create();
}
~FnGuid()
{
session_destroy(sess);
error_clear();
}
};
struct FnGuid2 : FnGuid
{
FnGuid2()
{
session_run_string(sess,
"solution 'MySolution';"
"prj = project 'MyProject';");
}
};
SUITE(engine)
{
/**************************************************************************
* Initial state tests
**************************************************************************/
TEST_FIXTURE(FnGuid, Guid_Exists_OnStartup)
TEST_FIXTURE(FxAccessor, Guid_Exists_OnStartup)
{
const char* result = session_run_string(sess,
"return (guid ~= nil)");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FnGuid, Guid_RaisesError_OnNoProject)
TEST_FIXTURE(FxAccessor, Guid_CanRoundtrip)
{
const char* result = session_run_string(sess, "guid '0C202E43-B9AF-4972-822B-5A42F0BF008C'");
CHECK_EQUAL("[string \"guid '0C202E43-B9AF-4972-822B-5A42F0BF008C'\"]:1: no active project", result);
}
TEST_FIXTURE(FnGuid2, Guid_ReturnsDefaultValue_OnNoValueSet)
{
const char* result = session_run_string(sess, "return guid()");
CHECK(result != NULL && guid_is_valid(result));
}
TEST_FIXTURE(FnGuid2, Guid_SetsField)
{
const char* result = session_run_string(sess,
const char* result = session_run_string(sess,
"guid '0C202E43-B9AF-4972-822B-5A42F0BF008C';"
"return prj.guid");
CHECK_EQUAL("0C202E43-B9AF-4972-822B-5A42F0BF008C", result);
}
TEST_FIXTURE(FnGuid2, Guid_ReturnsField_OnNoParams)
{
const char* result = session_run_string(sess,
"prj.guid = '0C202E43-B9AF-4972-822B-5A42F0BF008C';"
"return guid()");
CHECK_EQUAL("0C202E43-B9AF-4972-822B-5A42F0BF008C", result);
}
TEST_FIXTURE(FnGuid2, Guid_RaisesError_OnInvalidGuid)
TEST_FIXTURE(FxAccessor, Guid_RaisesError_OnInvalidGuid)
{
const char* result = session_run_string(sess, "guid '0C2XXXX-B9AF-4972-822B-5A42F0BF008C'");
CHECK_EQUAL("[string \"guid '0C2XXXX-B9AF-4972-822B-5A42F0BF008C'\"]:1: invalid GUID", result);
const char* result = session_run_string(sess,
"guid '0C202E43-XXXX-4972-822B-5A42F0BF008C'");
CHECK_EQUAL("invalid value '0C202E43-XXXX-4972-822B-5A42F0BF008C'", result);
}
}

View File

@ -0,0 +1,34 @@
/**
* \file fn_language_tests.cpp
* \brief Automated tests for the language() function.
* \author Copyright (c) 2007-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "accessor_tests.h"
SUITE(engine)
{
TEST_FIXTURE(FxAccessor, Language_Exists_OnStartup)
{
const char* result = session_run_string(sess,
"return (language ~= nil)");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FxAccessor, Language_CanRoundtrip)
{
const char* result = session_run_string(sess,
"language 'c++';"
"return language()");
CHECK_EQUAL("c++", result);
}
TEST_FIXTURE(FxAccessor, Language_RaisesError_OnInvalidLanguage)
{
const char* result = session_run_string(sess,
"language 'nosuch'");
CHECK_EQUAL("invalid value 'nosuch'", result);
}
}

View File

@ -74,7 +74,7 @@ SUITE(engine)
TEST_FIXTURE(FnProject, Project_Fails_OnNoActiveSolution)
{
const char* result = session_run_string(sess, "project('MyProject')");
CHECK_EQUAL("[string \"project('MyProject')\"]:1: no active solution", result);
CHECK_EQUAL("no active solution", result);
}
TEST_FIXTURE(FnProject2, Project_ReturnsNewObject_OnNewName)

View File

@ -8,6 +8,7 @@
#include "testing/testing.h"
extern "C" {
#include "engine/session.h"
#include "base/error.h"
}
@ -34,6 +35,8 @@ static int test_solution_fail(Session sess, Solution sln, Stream strm)
struct FxSession
{
Session sess;
Solution sln;
Project prj;
FxSession()
{
@ -45,14 +48,24 @@ struct FxSession
~FxSession()
{
session_destroy(sess);
error_clear();
}
Solution AddSolution()
{
Solution sln = solution_create();
sln = solution_create();
session_add_solution(sess, sln);
solution_set_name(sln, "MySolution");
return sln;
}
Project AddProject()
{
prj = project_create();
solution_add_project(sln, prj);
project_set_name(prj, "MyProject");
return prj;
}
};
@ -206,5 +219,42 @@ SUITE(session)
int result = session_unload(sess);
CHECK(result == OKAY);
}
/**********************************************************************
* Session validation tests
**********************************************************************/
TEST_FIXTURE(FxSession, Validate_ReturnsOkay_OnNoSolutions)
{
int result = session_validate(sess);
CHECK(result == OKAY);
}
TEST_FIXTURE(FxSession, Validate_ReturnsOkay_OnAllsWell)
{
AddSolution();
AddProject();
project_set_language(prj, "c++");
int result = session_validate(sess);
CHECK(result == OKAY);
}
TEST_FIXTURE(FxSession, Validate_NotOkay_OnEmptySolution)
{
AddSolution();
int result = session_validate(sess);
CHECK(result != OKAY);
CHECK_EQUAL("no projects defined for solution 'MySolution'", error_get());
}
TEST_FIXTURE(FxSession, Validate_NotOkay_OnNullLanguage)
{
AddSolution();
AddProject();
int result = session_validate(sess);
CHECK(result != OKAY);
CHECK_EQUAL("no language defined for project 'MyProject'", error_get());
}
}

View File

@ -27,6 +27,7 @@ struct FxUnloadProject
"solution('MySolution');"
"prj = project('MyProject');"
" guid '0C202E43-B9AF-4972-822B-5A42F0BF008C';"
" language 'c++';"
"return prj");
}
@ -60,5 +61,12 @@ SUITE(unload)
const char* result = project_get_guid(prj);
CHECK_EQUAL("0C202E43-B9AF-4972-822B-5A42F0BF008C", result);
}
TEST_FIXTURE(FxUnloadProject, UnloadProject_SetsLanguage)
{
unload_project(sess, L, prj);
const char* result = project_get_language(prj);
CHECK_EQUAL("c++", result);
}
}

View File

@ -127,27 +127,22 @@ int unload_solution(Session sess, lua_State* L, Solution sln)
int unload_project(Session sess, lua_State* L, Project prj)
{
const char* value;
int i;
assert(sess);
UNUSED(sess);
assert(L);
assert(prj);
sess = 0; /* unused */
lua_getfield(L, -1, "name");
value = lua_tostring(L, -1);
project_set_name(prj, value);
lua_pop(L, 1);
lua_getfield(L, -1, "basedir");
value = lua_tostring(L, -1);
project_set_base_dir(prj, value);
lua_pop(L, 1);
lua_getfield(L, -1, "guid");
value = lua_tostring(L, -1);
project_set_guid(prj, value);
lua_pop(L, 1);
for (i = 0; i < NumProjectFields; ++i)
{
lua_getfield(L, -1, ProjectFieldInfo[i].name);
value = lua_tostring(L, -1);
if (value != NULL)
{
project_set_value(prj, i, value);
}
lua_pop(L, 1);
}
return OKAY;
}

View File

@ -157,22 +157,3 @@ int host_show_help(Session sess)
return OKAY;
}
/**
* Make sure that the session contains a valid set of project objects: at least
* one solution, each solution contains at least one project, etc.
* \param sess The current execution session context.
* \returns OKAY if the session is valid.
*/
int host_validate_session(Session sess)
{
assert(sess);
if (session_num_solutions(sess) == 0)
{
error_set("no solutions defined");
return !OKAY;
}
return OKAY;
}

View File

@ -31,6 +31,5 @@ int host_run_steps(Session sess, HostExecutionStep* steps);
void host_set_argv(const char** argv);
int host_show_help(Session sess);
int host_tests(void);
int host_validate_session(Session sess);
#endif

View File

@ -1,63 +0,0 @@
/**
* \file host_validate_tests.cpp
* \brief Automated tests for session validation.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "testing/testing.h"
extern "C" {
#include "host/host.h"
#include "base/error.h"
}
struct FxHostValidate
{
Session sess;
Solution sln;
char buffer[8192];
FxHostValidate()
{
sess = session_create();
stream_set_buffer(Console, buffer);
}
~FxHostValidate()
{
session_destroy(sess);
error_clear();
host_set_argv(NULL);
}
void AddSolution()
{
sln = solution_create();
session_add_solution(sess, sln);
}
};
SUITE(host)
{
TEST_FIXTURE(FxHostValidate, Validate_ReturnsOkay_OnValidSession)
{
AddSolution();
int result = host_validate_session(sess);
CHECK(result == OKAY);
}
TEST_FIXTURE(FxHostValidate, Validate_ReturnsNotOkay_OnNoSolutions)
{
int result = host_validate_session(sess);
CHECK(result != OKAY);
}
TEST_FIXTURE(FxHostValidate, Validate_SetsError_OnNoSolutions)
{
host_validate_session(sess);
CHECK_EQUAL("no solutions defined", error_get());
}
}

View File

@ -18,8 +18,8 @@ static HostExecutionStep Steps[] =
host_parse_argv, /* process the command line arguments */
host_run_script, /* run the main script (i.e. premake4.lua) */
session_unload, /* unload the objects built by the script into more accessible C data structures */
session_validate, /* make sure that all required objects and values have been defined by the script */
host_show_help, /* show help and version messages as appropriate; may end processing here */
host_validate_session, /* make sure script defined required project objects */
host_run_action, /* run the action specified on the command line */
NULL /* all done! */
};

View File

@ -17,13 +17,22 @@ enum FieldKind
};
/**
* Field validation function signature.
* \param value The value to validate.
* \returns True if the value is considered valid.
*/
typedef int (*FieldValidator)(const char* value);
/**
* Metadata about a project object field.
*/
struct FieldInfo
{
const char* name; /**< The name of the field. */
enum FieldKind kind; /**< StringField or ListField */
const char* name; /**< The name of the field. */
enum FieldKind kind; /**< StringField or ListField */
FieldValidator validator; /**< The field validation function */
};

View File

@ -8,17 +8,20 @@
#include <stdlib.h>
#include "premake.h"
#include "project/project.h"
#include "base/cstr.h"
#include "base/guid.h"
#include "base/path.h"
#include "base/strings.h"
struct FieldInfo ProjectFieldInfo[] =
{
{ "basedir", StringField },
{ "guid", StringField },
{ "location", StringField },
{ "name", StringField },
{ 0, 0 }
{ "basedir", StringField, NULL },
{ "guid", StringField, guid_is_valid },
{ "language", StringField, project_is_valid_language },
{ "location", StringField, NULL },
{ "name", StringField, NULL },
{ 0, 0, NULL }
};
@ -112,14 +115,13 @@ const char* project_get_guid(Project prj)
/**
* Retrieve the programming language specified for a project.
* \param prj The project to query.
* \returns The programming language specified for the project.
* Get the programming language used by the project.
* \param prj The project object to query.
* \returns The language used by the project, or NULL if no language has been set.
*/
const char* project_get_language(Project prj)
{
prj = 0;
return "c++";
return project_get_value(prj, ProjectLanguage);
}
@ -158,6 +160,20 @@ const char* project_get_value(Project prj, enum ProjectField field)
}
/**
* Returns true if the specified language is recognized. Current valid language strings
* are 'c', 'c++', and 'c#'.
* \param language The language string.
* \returns True if the language string is recognized.
*/
int project_is_valid_language(const char* language)
{
return (cstr_eq(language, "c") ||
cstr_eq(language, "c++") ||
cstr_eq(language, "c#"));
}
/**
* Set the base directory of the project.
* \param prj The project object to modify.
@ -181,6 +197,17 @@ void project_set_guid(Project prj, const char* guid)
}
/**
* Set the programming language used by a project.
* \param prj The project to modify.
* \param language The programming language used by the project.
*/
void project_set_language(Project prj, const char* language)
{
project_set_value(prj, ProjectLanguage, language);
}
/**
* Set the output location (the relative path from the base directory to the
* target output directory) for this project.

View File

@ -17,6 +17,7 @@ enum ProjectField
{
ProjectBaseDirectory,
ProjectGuid,
ProjectLanguage,
ProjectLocation,
ProjectName,
NumProjectFields
@ -37,9 +38,11 @@ const char* project_get_language(Project prj);
const char* project_get_location(Project prj);
const char* project_get_name(Project prj);
const char* project_get_value(Project prj, enum ProjectField field);
int project_is_valid_language(const char* language);
void project_set_base_dir(Project prj, const char* base_dir);
void project_set_location(Project prj, const char* location);
void project_set_guid(Project prj, const char* guid);
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);
int project_tests(void);

View File

@ -17,10 +17,10 @@
struct FieldInfo SolutionFieldInfo[] =
{
{ "basedir", StringField },
{ "location", StringField },
{ "name", StringField },
{ 0, 0 }
{ "basedir", StringField, NULL },
{ "location", StringField, NULL },
{ "name", StringField, NULL },
{ 0, 0, NULL }
};

View File

@ -85,7 +85,7 @@ SUITE(project)
/**********************************************************************
* GUID testrs
* GUID tests
**********************************************************************/
TEST_FIXTURE(FxProject, GetGuid_ReturnsNull_OnStartup)
@ -102,6 +102,24 @@ SUITE(project)
}
/**********************************************************************
* Language tests
**********************************************************************/
TEST_FIXTURE(FxProject, GetLanguage_ReturnsNull_OnStartup)
{
const char* result = project_get_language(prj);
CHECK(result == NULL);
}
TEST_FIXTURE(FxProject, SetLanguage_CanRoundtrip)
{
project_set_language(prj, "c++");
const char* result = project_get_language(prj);
CHECK_EQUAL("c++", result);
}
/**********************************************************************
* Location tests
**********************************************************************/