Started implementation of projects

This commit is contained in:
starkos 2008-04-15 23:57:39 +00:00
parent 72f07dd0e8
commit 0cd1e2dd85
38 changed files with 1759 additions and 33 deletions

View File

@ -12,7 +12,7 @@ BUILDING PREMAKE
Until an official release is made you will need to use an existing
Premake 3.x executable to generate the project files. Grab a binary
or executable package from the project site at:
or source package from the project site at:
http://premake.sourceforge.net/
@ -20,11 +20,11 @@ BUILDING PREMAKE
$ cd premake-4.0
$ premake --target vs2005 # For Visual Studio 2005 files
$ premake --target gnu # For GNU makefile
Run `premake --help` for a complete list of supported toolsets.
SUPPORT
For questions, comments, or more information, visit the project

View File

@ -0,0 +1,225 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="CppExe"
ProjectGUID="{AE2461B7-236F-4278-81D3-F0D476F9A4C0}"
RootNamespace="CppExe"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\CppExe.cpp"
>
</File>
<File
RelativePath=".\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\stdafx.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
<File
RelativePath=".\ReadMe.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppExe", "CppExe\CppExe.vcproj", "{AE2461B7-236F-4278-81D3-F0D476F9A4C0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AE2461B7-236F-4278-81D3-F0D476F9A4C0}.Debug|Win32.ActiveCfg = Debug|Win32
{AE2461B7-236F-4278-81D3-F0D476F9A4C0}.Debug|Win32.Build.0 = Debug|Win32
{AE2461B7-236F-4278-81D3-F0D476F9A4C0}.Release|Win32.ActiveCfg = Release|Win32
{AE2461B7-236F-4278-81D3-F0D476F9A4C0}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -52,5 +52,3 @@ const char* make_get_solution_makefile(Session sess, Solution sln)
/* all good */
return my_path;
}

View File

@ -25,6 +25,8 @@ struct FxVs200xSln
stream_set_buffer(strm, buffer);
sln = solution_create();
solution_set_name(sln, "MySolution");
solution_set_base_dir(sln, "/Root");
}
~FxVs200xSln()
@ -33,6 +35,16 @@ struct FxVs200xSln
stream_destroy(strm);
session_destroy(sess);
}
Project AddProject()
{
Project prj = project_create();
project_set_name(prj, "MyProject");
project_set_base_dir(prj, "/Root");
project_set_guid(prj, "AE2461B7-236F-4278-81D3-F0D476F9A4C0");
solution_add_project(sln, prj);
return prj;
}
};
@ -70,4 +82,31 @@ SUITE(action)
"# Visual Studio 2005\r\n",
buffer);
}
/**********************************************************************
* Project entry tests
**********************************************************************/
TEST_FIXTURE(FxVs200xSln, ProjectEntry_IsCorrect_OnCppProject)
{
AddProject();
vs200x_solution_projects(sess, sln, strm);
CHECK_EQUAL(
"Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"MyProject\", \"MyProject.vcproj\", \"{AE2461B7-236F-4278-81D3-F0D476F9A4C0}\"\n"
"EndProject\n",
buffer);
}
TEST_FIXTURE(FxVs200xSln, ProjectEntry_UsesRelativePath)
{
Project prj = AddProject();
project_set_location(prj, "ProjectFolder");
vs200x_solution_projects(sess, sln, strm);
CHECK_EQUAL(
"Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"MyProject\", \"ProjectFolder\\MyProject.vcproj\", \"{AE2461B7-236F-4278-81D3-F0D476F9A4C0}\"\n"
"EndProject\n",
buffer);
}
}

View File

@ -15,6 +15,7 @@ static SessionSolutionCallback Vs2005SolutionCallbacks[] =
{
vs200x_solution_create,
vs200x_solution_signature,
vs200x_solution_projects,
NULL
};

View File

@ -0,0 +1,20 @@
/**
* \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

@ -0,0 +1,13 @@
/**
* \file vs200x_project.h
* \brief Visual Studio 200x project generation functions.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#if !defined(PREMAKE_VS200X_PROJECT_H)
#define PREMAKE_VS200X_PROJECT_H
#include "engine/session.h"
const char* vs200x_project_extension(Project prj);
#endif

View File

@ -9,7 +9,10 @@
#include "premake.h"
#include "vs200x.h"
#include "vs200x_solution.h"
#include "vs200x_project.h"
#include "base/cstr.h"
#include "base/error.h"
#include "base/path.h"
/**
@ -35,6 +38,44 @@ int vs200x_solution_create(Session sess, Solution sln, Stream strm)
}
/**
* Write out the list of projects contained by the solution.
* \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 vs200x_solution_projects(Session sess, Solution sln, Stream strm)
{
const char* sln_path;
int i, n;
sess = 0; /* unused */
/* project file paths are specified relative to the solution */
sln_path = path_directory(solution_get_filename(sln, NULL, NULL));
n = solution_num_projects(sln);
for (i = 0; i < n; ++i)
{
Project prj = solution_get_project(sln, i);
const char* prj_name = project_get_name(prj);
const char* prj_id = project_get_guid(prj);
const char* prj_ext = vs200x_project_extension(prj);
const char* prj_file = project_get_filename(prj, prj_name, prj_ext);
const char* tool_id = vs200x_solution_tool_guid("c++");
/* convert absolute project file name to be relative to solution */
prj_file = path_relative(sln_path, prj_file);
prj_file = path_translate(prj_file, "\\");
stream_writeline(strm, "Project(\"{%s}\") = \"%s\", \"%s\", \"{%s}\"", tool_id, prj_name, prj_file, prj_id);
stream_writeline(strm, "EndProject");
}
return OKAY;
}
/**
* Write the solution file signature block.
* \param sess The execution session context.
@ -71,3 +112,26 @@ int vs200x_solution_signature(Session sess, Solution sln, Stream strm)
return z;
}
/**
* Returns the Visual Studio GUID for a particular project type.
* \param language The programming language used in the project.
* \returns The GUID corresponding the programming language.
*/
const char* vs200x_solution_tool_guid(const char* language)
{
if (cstr_eq(language, "c") || cstr_eq(language, "c++"))
{
return "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942";
}
else if (cstr_eq(language, "c#"))
{
return "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC";
}
else
{
error_set("unsupported language '%s'", language);
return NULL;
}
}

View File

@ -8,7 +8,9 @@
#include "engine/session.h"
int vs200x_solution_create(Session sess, Solution sln, Stream strm);
int vs200x_solution_signature(Session sess, Solution sln, Stream strm);
int vs200x_solution_create(Session sess, Solution sln, Stream strm);
int vs200x_solution_projects(Session sess, Solution sln, Stream strm);
int vs200x_solution_signature(Session sess, Solution sln, Stream strm);
const char* vs200x_solution_tool_guid(const char* language);
#endif

109
src/base/guid.c Normal file
View File

@ -0,0 +1,109 @@
/**
* \file guid.c
* \brief GUID creation and validation.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#include <stdio.h>
#include <string.h>
#include "premake.h"
#include "guid.h"
#include "platform/platform.h"
#include "base/buffers.h"
static void stringify(char* src, char* dst, int count);
/**
* Create a new GUID, with the format "4E67EBCE-BC8B-4058-9AA9-48EE5E003683".
* \returns The new GUID.
*/
const char* guid_create()
{
char guid[16];
char* result = buffers_next();
/* get a GUID as an array of 16 bytes */
platform_create_guid(guid);
/* convert that array to a string in the usual format */
stringify(guid, result, 4);
result[8] = '-';
stringify(guid + 4, result + 9, 2);
result[13] = '-';
stringify(guid + 6, result + 14, 2);
result[18] = '-';
stringify(guid + 8, result + 19, 2);
result[23] = '-';
stringify(guid + 10, result + 24, 6);
result[36] = '\0';
return result;
}
/**
* Validate the format of a GUID, which should use the form "4E67EBCE-BC8B-4058-9AA9-48EE5E003683".
* \param value The guid to validate.
* \returns True if valid, zero otherwise.
*/
int guid_is_valid(const char* value)
{
int i, n;
/* make sure it is the right size */
if (strlen(value) != 36)
return 0;
/* check for dashes in the right places */
if (value[8] != '-' ||
value[13] != '-' ||
value[18] != '-' ||
value[23] != '-')
{
return 0;
}
/* make sure only [0-9A-F-] are present; count the number of dashes on the way */
n = 0;
for (i = 0; i < 36; ++i)
{
if (value[i] == '-')
{
++n;
}
else if ((value[i] < '0' || value[i] > '9') &&
(value[i] < 'A' || value[i] > 'F') &&
(value[i] < 'a' || value[i] > 'f'))
{
return 0;
}
}
/* make sure I've got the right number of dashes */
if (n != 4)
{
return 0;
}
return 1;
}
/**
* Convert an array of bytes to a string.
* \param src The source array of bytes.
* \param dst The destination string buffer.
* \param count The number of bytes to convert.
*/
static void stringify(char* src, char* dst, int count)
{
int i;
for (i = 0; i < count; ++i)
{
unsigned value = (unsigned char)src[i];
sprintf(dst, "%02X", value);
dst += 2;
}
}

12
src/base/guid.h Normal file
View File

@ -0,0 +1,12 @@
/**
* \file guid.h
* \brief GUID creation and validation.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#if !defined(PREMAKE_GUID_H)
#define PREMAKE_GUID_H
const char* guid_create();
int guid_is_valid(const char* value);
#endif

View File

@ -145,6 +145,10 @@ char* path_join(const char* leading, const char* trailing)
{
char* buffer = buffers_next();
/* treat nulls like empty paths */
leading = (leading != NULL) ? leading : "";
trailing = (trailing != NULL) ? trailing : "";
if (!trailing)
{
strcpy(buffer, leading);
@ -172,6 +176,61 @@ char* path_join(const char* leading, const char* trailing)
}
/**
* \brief Compute the relative path between two locations.
* \param base The base path.
* \param target The target path.
* \returns A relative path from the base to the target.
*/
char* path_relative(const char* base, const char* target)
{
int start, i;
char* result;
/* normalize the two paths */
char* full_base = path_absolute(base);
char* full_targ = path_absolute(target);
strcat(full_base, "/");
strcat(full_targ, "/");
/* trim off the common directories from the start */
for (start = 0, i = 0; full_base[i] && full_targ[i] && full_base[i] == full_targ[i]; ++i)
{
if (full_base[i] == '/')
start = i + 1;
}
/* same directory? */
if (full_base[i] == 0 && full_targ[i] == 0)
return ".";
/* build a connecting path */
result = buffers_next();
if (strlen(full_base) - start > 0)
{
strcpy(result, "../");
for (i = start; full_base[i]; ++i)
{
if (full_base[i] == '/' && full_base[i + 1])
strcat(result, "../");
}
}
if (strlen(full_targ) - start > 0)
{
strcat(result, full_targ + start);
}
/* remove the trailing slash */
result[strlen(result) - 1] = 0;
if (strlen(result) == 0)
strcpy(result, ".");
return result;
}
/**
* Replace all path separator characters in a path.
* \param path The path to translate.

View File

@ -11,6 +11,7 @@ char* path_assemble(const char* dir, const char* filename, const char* ext);
char* path_directory(const char* path);
int path_is_absolute(const char* path);
char* path_join(const char* leading, const char* trailing);
char* path_relative(const char* base, const char* target);
char* path_translate(const char* path, const char* sep);
#endif

View File

@ -0,0 +1,86 @@
/**
* \file guid_tests.cpp
* \brief Automated tests for GUID generation and validation.
* \author Copyright (c) 2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "testing/testing.h"
extern "C" {
#include "base/guid.h"
}
SUITE(base)
{
TEST(GuidCreate_ReturnsCorrectSize)
{
const char* guid = guid_create();
CHECK(guid != NULL && strlen(guid) == 36);
}
TEST(GuidCreate_CorrectDashes)
{
const char* guid = guid_create();
CHECK(guid[8]=='-' && guid[13]=='-' && guid[18]=='-' && guid[23]=='-');
}
TEST(GuidCreate_CorrectSymbols)
{
for (const char* guid = guid_create(); *guid; ++guid)
{
const char ch = *guid;
CHECK((ch>='0' && ch<='9') || (ch>='A' && ch<='F') || (ch=='-'));
}
}
TEST(GuidCreate_CorrectNumOfDashes)
{
int num = 0;
for (const char* guid = guid_create(); *guid; ++guid)
{
if (*guid=='-') ++num;
}
CHECK(num == 4);
}
TEST(GuidIsValid_ReturnsTrue_OnNoBraces)
{
CHECK(guid_is_valid("4E67EBCE-BC8B-4058-9AA9-48EE5E003683"));
}
TEST(GuidIsValid_ReturnsFalse_OnTooShort)
{
CHECK(!guid_is_valid("4E67EBCE-BC8B-4058-9AA9-48EE"));
}
TEST(GuidIsValid_ReturnsFalse_OnMissingFirstDash)
{
CHECK(!guid_is_valid("4E67EBCE BC8B-4058-9AA9-48EE5E003683"));
}
TEST(GuidIsValid_ReturnsFalse_OnMissingSecondDash)
{
CHECK(!guid_is_valid("4E67EBCE-BC8B 4058-9AA9-48EE5E003683"));
}
TEST(GuidIsValid_ReturnsFalse_OnMissingThirdDash)
{
CHECK(!guid_is_valid("4E67EBCE-BC8B-4058 9AA9-48EE5E003683"));
}
TEST(GuidIsValid_ReturnsFalse_OnMissingLastDash)
{
CHECK(!guid_is_valid("4E67EBCE-BC8B-4058-9AA9 48EE5E003683"));
}
TEST(GuidIsValid_ReturnsFalse_OnTooManyDashes)
{
CHECK(!guid_is_valid("4E67EBCE-BC8B-4058-9AA9-48EE5-003683"));
}
TEST(GuidIsValid_ReturnsFalse_OnInvalidChar)
{
CHECK(!guid_is_valid("XE67EBCE-BC8B-4058-9AA9-48EE5X003683"));
}
}

View File

@ -1,7 +1,7 @@
/**
* \file path_tests.cpp
* \brief Path handling automated tests.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
* \author Copyright (c) 2007-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
@ -115,6 +115,38 @@ SUITE(base)
}
/**************************************************************************
* path_relative() tests
**************************************************************************/
TEST(PathRelative_ReturnsDot_OnMatchingPaths)
{
char* result = path_relative("/a/b/c", "/a/b/c");
CHECK_EQUAL(".", result);
}
TEST(PathRelative_ReturnsDoubleDot_OnChildToParent)
{
char* result = path_relative("/a/b/c", "/a/b");
CHECK_EQUAL("..", result);
}
TEST(PathRelative_ReturnsDoubleDotPath_OnSiblingToSibling)
{
char* result = path_relative("/a/b/c", "/a/b/d");
CHECK_EQUAL("../d", result);
}
TEST(PathRelative_ReturnsChildPath_OnParentToChild)
{
char* result = path_relative("/a/b/c", "/a/b/c/d");
CHECK_EQUAL("d", result);
}
/**************************************************************************
* path_translate() tests
**************************************************************************/

View File

@ -11,6 +11,28 @@
#include "base/path.h"
/**
* Configure a new project object (solution, project). Initializes all list
* fields and creates an initial configuration list.
* \param L The Lua state.
* \param fields The list of object fields.
*/
void engine_configure_project_object(lua_State* L, struct FieldInfo* fields)
{
struct FieldInfo* field;
/* set all list-type configuration values to empty tables */
for (field = fields; field->name != NULL; ++field)
{
if (field->kind == ListField)
{
lua_newtable(L);
lua_setfield(L, -2, field->name);
}
}
}
/**
* Pushes the active value for the given object type to the top of the stack.
* This function is used to retrieve the current solution, project, etc.
@ -101,6 +123,25 @@ int engine_get_active_object(lua_State* L, enum ObjectType type, int is_required
}
/**
* Get the directory which contains the currently executing script. This is
* used to locate resources specified in the script using relative paths.
* \param L The Lua state.
* \returns The directory containing the current script, as an absolute path.
*/
const char* engine_get_script_dir(lua_State* L)
{
const char* path;
lua_getglobal(L, FILE_KEY);
path = lua_tostring(L, -1);
lua_pop(L, 1);
path = path_directory(path);
return path;
}
/**
* Remembers the object at the top of the stack as active for the given object type.
* This function is used to indicate the current solution, project, etc.
@ -143,20 +184,3 @@ void engine_set_script_file(lua_State* L, const char* filename)
}
/**
* Get the directory which contains the currently executing script. This is
* used to locate resources specified in the script using relative paths.
* \param L The Lua state.
* \returns The directory containing the current script, as an absolute path.
*/
const char* engine_get_script_dir(lua_State* L)
{
const char* path;
lua_getglobal(L, FILE_KEY);
path = lua_tostring(L, -1);
lua_pop(L, 1);
path = path_directory(path);
return path;
}

39
src/engine/fn_guid.c Normal file
View File

@ -0,0 +1,39 @@
/**
* \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;
}

72
src/engine/fn_project.c Normal file
View File

@ -0,0 +1,72 @@
/**
* \file fn_project.c
* \brief Create or select a project object.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "internals.h"
#include "base/guid.h"
/**
* Create a new project object, or select an existing one.
*/
int fn_project(lua_State* L)
{
const char* name;
/* if there are no parameters, return the active project */
if (lua_gettop(L) == 0)
{
engine_get_active_object(L, ProjectObject, OPTIONAL);
return 1;
}
/* get the active solution, which will contain this project */
if (!engine_get_active_object(L, SolutionObject, REQUIRED))
{
return 0;
}
name = luaL_checkstring(L, 1);
/* get the projects list from the solution */
lua_getfield(L, -1, PROJECTS_KEY);
/* check to see if a project with this name already exists */
lua_getfield(L, -1, name);
if (lua_isnil(L, -1))
{
/* project does not exists, create it */
lua_newtable(L);
/* set the name */
lua_pushvalue(L, 1);
lua_setfield(L, -2, ProjectFieldInfo[ProjectName].name);
/* set the base directory */
lua_pushstring(L, engine_get_script_dir(L));
lua_setfield(L, -2, ProjectFieldInfo[ProjectBaseDirectory].name);
/* set a default GUID */
lua_pushstring(L, guid_create());
lua_setfield(L, -2, ProjectFieldInfo[ProjectGuid].name);
/* finish the configuration */
engine_configure_project_object(L, ProjectFieldInfo);
/* add it to solution's list of projects, keyed by name */
lua_pushvalue(L, -1);
lua_setfield(L, -4, name);
/* also add with integer key */
lua_pushvalue(L, -1);
lua_rawseti(L, -4, luaL_getn(L, -4) + 1);
}
/* activate and return the solution object */
engine_set_active_object(L, ProjectObject);
return 1;
}

View File

@ -9,7 +9,7 @@
/**
* Creates a new solution object, or selects an existing one.
* Create a new solution object, or select an existing one.
*/
int fn_solution(lua_State* L)
{
@ -34,11 +34,18 @@ int fn_solution(lua_State* L)
/* set the name */
lua_pushstring(L, name);
lua_setfield(L, -2, "name");
lua_setfield(L, -2, SolutionFieldInfo[SolutionName].name);
/* set the base directory */
lua_pushstring(L, engine_get_script_dir(L));
lua_setfield(L, -2, "basedir");
lua_setfield(L, -2, SolutionFieldInfo[SolutionBaseDirectory].name);
/* create an empty list of projects */
lua_newtable(L);
lua_setfield(L, -2, PROJECTS_KEY);
/* finish the configuration */
engine_configure_project_object(L, SolutionFieldInfo);
/* add it to the master list of solutions, keyed by name */
lua_pushvalue(L, -1);

View File

@ -19,6 +19,7 @@
#define CONFIGURATION_KEY "configuration"
#define FILE_KEY "_FILE"
#define PROJECT_KEY "project"
#define PROJECTS_KEY "projects"
#define SESSION_KEY "_SESSION"
#define SOLUTION_KEY "solution"
#define SOLUTIONS_KEY "_SOLUTIONS"
@ -41,6 +42,7 @@ int engine_get_active_object(lua_State* L, enum ObjectType type, int is_
void engine_set_active_object(lua_State* L, enum ObjectType type);
void engine_set_script_file(lua_State* L, const char* filename);
const char* engine_get_script_dir(lua_State* L);
void engine_configure_project_object(lua_State* L, struct FieldInfo* fields);
/* Internal session API */
lua_State* session_get_lua_state(Session sess);
@ -51,17 +53,21 @@ lua_State* session_get_lua_state(Session sess);
struct UnloadFuncs
{
int (*unload_solution)(Session sess, lua_State* L, Solution sln);
int (*unload_project)(Session sess, lua_State* L, Project prj);
};
int unload_all(Session sess, lua_State* L, struct UnloadFuncs* funcs);
int unload_solution(Session sess, lua_State* L, Solution sln);
int unload_project(Session sess, lua_State* L, Project prj);
/* Script function handlers */
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);

View File

@ -18,7 +18,9 @@
/** 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 },
{ NULL, NULL }
};
@ -350,6 +352,7 @@ int session_unload(Session sess)
assert(sess);
funcs.unload_solution = unload_solution;
funcs.unload_project = unload_project;
result = unload_all(sess, sess->L, &funcs);
return result;
}

View File

@ -0,0 +1,88 @@
/**
* \file fn_guid_tests.cpp
* \brief Automated tests for the guid() function.
* \author Copyright (c) 2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "testing/testing.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)
{
const char* result = session_run_string(sess,
"return (guid ~= nil)");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FnGuid, Guid_RaisesError_OnNoProject)
{
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,
"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)
{
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);
}
}

View File

@ -0,0 +1,145 @@
/**
* \file fn_project_tests.cpp
* \brief Automated tests for the project() function.
* \author Copyright (c) 2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "testing/testing.h"
extern "C" {
#include "engine/session.h"
#include "base/error.h"
}
struct FnProject
{
Session sess;
FnProject()
{
sess = session_create();
}
~FnProject()
{
session_destroy(sess);
error_clear();
}
};
struct FnProject2
{
Session sess;
FnProject2()
{
sess = session_create();
session_run_string(sess,
"sln = solution('MySolution');"
"prj = project('MyProject')");
}
~FnProject2()
{
session_destroy(sess);
error_clear();
}
};
SUITE(engine)
{
/**************************************************************************
* Initial state tests
**************************************************************************/
TEST_FIXTURE(FnProject, Project_Exists_OnStartup)
{
const char* result = session_run_string(sess,
"return (project ~= nil)");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FnProject, Project_ReturnsNil_OnNoActiveProject)
{
const char* result = session_run_string(sess,
"return (project() == nil)");
CHECK_EQUAL("true", result);
}
/**************************************************************************
* Object creation tests
**************************************************************************/
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);
}
TEST_FIXTURE(FnProject2, Project_ReturnsNewObject_OnNewName)
{
const char* result = session_run_string(sess,
"return (prj ~= nil)");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FnProject2, Project_ReturnsObject_OnActiveProject)
{
const char* result = session_run_string(sess,
"return (prj == project())");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FnProject2, Project_AddsToKeyList_OnNewName)
{
const char* result = session_run_string(sess,
"return (prj == sln.projects['MyProject']);");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FnProject2, Project_AddsToIndexList_OnNewName)
{
const char* result = session_run_string(sess,
"return (prj == sln.projects[1]);");
CHECK_EQUAL("true", result);
}
TEST_FIXTURE(FnProject2, Project_IncrementsTableSize_OnNewName)
{
const char* result = session_run_string(sess,
"return #sln.projects");
CHECK_EQUAL("1", result);
}
TEST_FIXTURE(FnProject2, Project_ReturnsSameObject_OnExistingName)
{
const char* result = session_run_string(sess,
"prj1 = project('SecondProject');"
"return (prj == project('MyProject'))");
CHECK_EQUAL("true", result);
}
/**************************************************************************
* Initial object state tests
**************************************************************************/
TEST_FIXTURE(FnProject2, Project_SetsName)
{
const char* result = session_run_string(sess, "return prj.name");
CHECK_EQUAL("MyProject", result);
}
TEST_FIXTURE(FnProject2, Project_SetsBaseDir)
{
const char* result = session_run_string(sess, "return prj.basedir");
CHECK_EQUAL("(string)", result);
}
TEST_FIXTURE(FnProject2, Project_SetsGuid)
{
const char* result = session_run_string(sess, "return prj.guid");
CHECK(result != NULL && strlen(result) == 36);
}
}

View File

@ -1,6 +1,6 @@
/**
* \file fn_solution_tests.cpp
* \brief Automated test for the solution() function.
* \brief Automated tests for the solution() function.
* \author Copyright (c) 2007-2008 Jason Perkins and the Premake project
*/
@ -117,6 +117,7 @@ SUITE(engine)
/**************************************************************************
* Initial object state tests
**************************************************************************/
TEST_FIXTURE(FnSolution2, Solution_SetsName)
{
const char* result = session_run_string(sess,
@ -130,4 +131,11 @@ SUITE(engine)
"return sln.basedir");
CHECK_EQUAL("(string)", result);
}
TEST_FIXTURE(FnSolution2, Solution_HasEmptyProjectsList)
{
const char* result = session_run_string(sess,
"return #sln.projects");
CHECK_EQUAL("0", result);
}
}

View File

@ -0,0 +1,64 @@
/**
* \file unload_project_tests.cpp
* \brief Automated tests for project object unloading from the script environment.
* \author Copyright (c) 2008 Jason Perkins and the Premake project
*/
#include "premake.h"
#include "testing/testing.h"
extern "C" {
#include "engine/internals.h"
}
struct FxUnloadProject
{
Session sess;
lua_State* L;
Project prj;
FxUnloadProject()
{
sess = session_create();
L = session_get_lua_state(sess);
prj = project_create();
session_run_string(sess,
"solution('MySolution');"
"prj = project('MyProject');"
" guid '0C202E43-B9AF-4972-822B-5A42F0BF008C';"
"return prj");
}
~FxUnloadProject()
{
project_destroy(prj);
session_destroy(sess);
}
};
SUITE(unload)
{
TEST_FIXTURE(FxUnloadProject, UnloadProject_SetsName)
{
unload_project(sess, L, prj);
const char* result = project_get_name(prj);
CHECK_EQUAL("MyProject", result);
}
TEST_FIXTURE(FxUnloadProject, UnloadProject_SetsBaseDir)
{
unload_project(sess, L, prj);
const char* result = project_get_base_dir(prj);
CHECK_EQUAL("(string)", result);
}
TEST_FIXTURE(FxUnloadProject, UnloadProject_SetsGuid)
{
unload_project(sess, L, prj);
const char* result = project_get_guid(prj);
CHECK_EQUAL("0C202E43-B9AF-4972-822B-5A42F0BF008C", result);
}
}

View File

@ -1,6 +1,6 @@
/**
* \file unload_solution_tests.cpp
* \brief Automated test for solution object unloading from the script environment.
* \brief Automated tests for solution object unloading from the script environment.
* \author Copyright (c) 2007-2008 Jason Perkins and the Premake project
*/

View File

@ -13,6 +13,7 @@ extern "C" {
/* mock interface to object loaders */
static int num_solution_calls;
static int num_project_calls;
static int stub_solution_func(Session sess, lua_State* L, Solution sln)
{
@ -28,6 +29,20 @@ static int stub_solution_fail_func(Session sess, lua_State* L, Solution sln)
return !OKAY;
}
static int stub_project_func(Session sess, lua_State* L, Project prj)
{
sess = NULL; L = NULL; prj = NULL;
num_project_calls++;
return OKAY;
}
static int stub_project_fail_func(Session sess, lua_State* L, Project prj)
{
sess = NULL; L = NULL; prj = NULL;
num_project_calls++;
return !OKAY;
}
struct FxUnload
{
@ -40,7 +55,9 @@ struct FxUnload
sess = session_create();
L = session_get_lua_state(sess);
funcs.unload_solution = stub_solution_func;
funcs.unload_project = stub_project_func;
num_solution_calls = 0;
num_project_calls = 0;
}
~FxUnload()
@ -56,6 +73,8 @@ struct FxUnload2 : FxUnload
{
session_run_string(sess,
"solution 'MySolution';"
" project 'MyProject';"
" project 'MyProject2';"
"solution 'MySolution2';");
}
@ -98,7 +117,7 @@ SUITE(unload)
CHECK(n == 2);
}
TEST_FIXTURE(FxUnload2, Unload_CallsSolutionFunc_OnEachSession)
TEST_FIXTURE(FxUnload2, Unload_CallsSolutionFunc_OnEachSolution)
{
unload_all(sess, L, &funcs);
CHECK(num_solution_calls == 2);
@ -117,4 +136,38 @@ SUITE(unload)
unload_all(sess, L, &funcs);
CHECK(num_solution_calls == 1);
}
/**********************************************************************
* Project enumeration tests
**********************************************************************/
TEST_FIXTURE(FxUnload2, Unload_AddsProjects_OnNonEmptySession)
{
unload_all(sess, L, &funcs);
Solution sln = session_get_solution(sess, 0);
int n = solution_num_projects(sln);
CHECK(n == 2);
}
TEST_FIXTURE(FxUnload2, Unload_CallsProjectFunc_OnEachProject)
{
unload_all(sess, L, &funcs);
CHECK(num_project_calls == 2);
}
TEST_FIXTURE(FxUnload2, Unload_ReturnsNotOkay_OnProjectFailure)
{
funcs.unload_project = stub_project_fail_func;
int result = unload_all(sess, L, &funcs);
CHECK(result != OKAY);
}
TEST_FIXTURE(FxUnload2, Unload_AbortsProjectLoop_OnNotOkay)
{
funcs.unload_project = stub_project_fail_func;
unload_all(sess, L, &funcs);
CHECK(num_project_calls == 1);
}
}

View File

@ -37,10 +37,34 @@ int unload_all(Session sess, lua_State* L, struct UnloadFuncs* funcs)
lua_rawgeti(L, -1, si);
status = funcs->unload_solution(sess, L, sln);
if (status == OKAY)
{
/* iterate over list of projects */
int pi, pn;
lua_getfield(L, -1, PROJECTS_KEY);
pn = luaL_getn(L, -1);
for (pi = 1; status == OKAY && pi <= pn; ++pi)
{
Project prj = project_create();
solution_add_project(sln, prj);
lua_rawgeti(L, -1, pi);
status = funcs->unload_project(sess, L, prj);
/* remove project object from stack */
lua_pop(L, 1);
}
/* remove list of projects from stack */
lua_pop(L, 1);
}
/* remove solution object from stack */
lua_pop(L, 1);
}
/* remove list of solutions from stack */
lua_pop(L, 1);
return status;
}
@ -60,7 +84,7 @@ int unload_solution(Session sess, lua_State* L, Solution sln)
assert(L);
assert(sln);
sess = 0;
sess = 0; /* unused */
lua_getfield(L, -1, "name");
value = lua_tostring(L, -1);
@ -74,3 +98,39 @@ int unload_solution(Session sess, lua_State* L, Solution sln)
return OKAY;
}
/**
* Unload information from the scripting environment for a particular project.
* \param sess The session object which contains the scripted project objects.
* \param L The Lua scripting engine state.
* \param prj The project object to be populated.
* \returns OKAY if successful.
*/
int unload_project(Session sess, lua_State* L, Project prj)
{
const char* value;
assert(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);
return OKAY;
}

View File

@ -27,6 +27,13 @@
int platform_create_dir(const char* path);
/**
* Create a GUID and copy it into the supplied buffer.
* \param buffer The buffer to hold the new GUID; must hold at least 36 characters.
*/
void platform_create_guid(char* buffer);
/**
* Get the current working directory.
* \param buffer A buffer to hold the directory.

View File

@ -25,6 +25,15 @@ int platform_create_dir(const char* path)
}
void platform_create_guid(char* buffer)
{
/* not sure how to get a UUID here, so I fake it */
FILE* rnd = fopen("/dev/random", "rb");
fread(buffer, 16, 1, rnd);
fclose(rnd);
}
int platform_dir_get_current(char* buffer, int size)
{
char* result = getcwd(buffer, size);

View File

@ -18,6 +18,18 @@ int platform_create_dir(const char* path)
}
void platform_create_guid(char* buffer)
{
static int (__stdcall *CoCreateGuid)(char*) = NULL;
if (CoCreateGuid == NULL)
{
HMODULE hOleDll = LoadLibrary("OLE32.DLL");
CoCreateGuid = (int(__stdcall*)(char*))GetProcAddress(hOleDll, "CoCreateGuid");
}
CoCreateGuid(buffer);
}
int platform_dir_get_current(char* buffer, int size)
{
DWORD result = GetCurrentDirectory(size, buffer);

205
src/project/project.c Normal file
View File

@ -0,0 +1,205 @@
/**
* \file project.c
* \brief The project class.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
*/
#include <assert.h>
#include <stdlib.h>
#include "premake.h"
#include "project/project.h"
#include "base/path.h"
#include "base/strings.h"
struct FieldInfo ProjectFieldInfo[] =
{
{ "basedir", StringField },
{ "guid", StringField },
{ "location", StringField },
{ "name", StringField },
{ 0, 0 }
};
DEFINE_CLASS(Project)
{
Fields fields;
};
/**
* Create and initialize a new project object.
* \returns A new project object.
*/
Project project_create()
{
Project prj = ALLOC_CLASS(Project);
prj->fields = fields_create(ProjectFieldInfo);
return prj;
}
/**
* Destroy a project object and release the associated memory.
* \param prj The project object to destroy.
*/
void project_destroy(Project prj)
{
assert(prj);
fields_destroy(prj->fields);
free(prj);
}
/**
* Get the base directory for the project; any properties containing relative
* paths are relative to this location.
* \param prj The project object to query.
* \returns The base directory, or NULL if no directory has been set.
*/
const char* project_get_base_dir(Project prj)
{
return project_get_value(prj, ProjectBaseDirectory);
}
/**
* Get the path to the project output file, using the provided file extension.
* \param prj The project object to query.
* \param basename The base filename; if NULL the project name will be used.
* \param ext The file extension to be used on the filename; may be NULL.
* \returns The path to the project file.
*/
const char* project_get_filename(Project prj, const char* basename, const char* ext)
{
const char* base_dir;
const char* location;
const char* directory;
const char* result;
assert(prj);
if (!basename)
{
basename = project_get_name(prj);
}
if (!ext)
{
ext = "";
}
base_dir = project_get_base_dir(prj);
location = project_get_location(prj);
directory = path_join(base_dir, location);
result = path_assemble(directory, basename, ext);
return result;
}
/**
* 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)
{
return project_get_value(prj, ProjectGuid);
}
/**
* Retrieve the output location (the relative path from the base directory to the
* target output directory) for this project.
* \param prj The project object to modify.
* \returns The project output location, or NULL if no location has been set.
*/
const char* project_get_location(Project prj)
{
return project_get_value(prj, ProjectLocation);
}
/**
* Get the name of the project.
* \returns The name, if set, NULL otherwise.
*/
const char* project_get_name(Project prj)
{
return project_get_value(prj, ProjectName);
}
/**
* Retrieve a string (single value) fields from a project, using the field indices.
* \param prj The project object to query.
* \param field The index of the field to query.
* \returns The value of the field if set, of NULL.
*/
const char* project_get_value(Project prj, enum ProjectField field)
{
assert(prj);
return fields_get_value(prj->fields, field);
}
/**
* Set the base directory of the project.
* \param prj The project object to modify.
* \param base_dir The new base directory.
*/
void project_set_base_dir(Project prj, const char* base_dir)
{
project_set_value(prj, ProjectBaseDirectory, base_dir);
}
/**
* Set the GUID associated with a project. The GUID is required by the Visual
* Studio generators, and must be unique per project.
* \param prj The project to modify.
* \param guid The new project GUID.
*/
void project_set_guid(Project prj, const char* guid)
{
project_set_value(prj, ProjectGuid, guid);
}
/**
* Set the output location (the relative path from the base directory to the
* target output directory) for this project.
* \param prj The project object to modify.
* \param location The new output location.
*/
void project_set_location(Project prj, const char* location)
{
project_set_value(prj, ProjectLocation, location);
}
/**
* Set the name of the project.
* \param prj The project object.
* \param name The new for the project.
*/
void project_set_name(Project prj, const char* name)
{
project_set_value(prj, ProjectName, name);
}
/**
* Set a string (single value) field on a project, using the field indices.
* \param prj The project object.
* \param field The field to set.
* \param value The new value for the field.
*/
void project_set_value(Project prj, enum ProjectField field, const char* value)
{
assert(prj);
fields_set_value(prj->fields, field, value);
}

View File

@ -6,8 +6,41 @@
#if !defined(PREMAKE_PROJECT_H)
#define PREMAKE_PROJECT_H
#include "fields.h"
/**
* Project field index.
* \note If you modify this list, you must also update SolutionFieldInfo[].
*/
enum ProjectField
{
ProjectBaseDirectory,
ProjectGuid,
ProjectLocation,
ProjectName,
NumProjectFields
};
extern struct FieldInfo ProjectFieldInfo[];
DECLARE_CLASS(Project)
int project_tests(void);
Project project_create(void);
void project_destroy(Project prj);
const char* project_get_base_dir(Project prj);
const char* project_get_filename(Project prj, const char* basename, const char* ext);
const char* project_get_guid(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);
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_name(Project prj, const char* name);
void project_set_value(Project prj, enum ProjectField field, const char* value);
int project_tests(void);
#endif

View File

@ -8,6 +8,7 @@
#include <stdlib.h>
#include "premake.h"
#include "project/solution.h"
#include "base/array.h"
#include "base/path.h"
#include "base/strings.h"
@ -26,6 +27,7 @@ struct FieldInfo SolutionFieldInfo[] =
DEFINE_CLASS(Solution)
{
Fields fields;
Array projects;
};
@ -37,6 +39,7 @@ Solution solution_create()
{
Solution sln = ALLOC_CLASS(Solution);
sln->fields = fields_create(SolutionFieldInfo);
sln->projects = array_create();
return sln;
}
@ -47,12 +50,36 @@ Solution solution_create()
*/
void solution_destroy(Solution sln)
{
int i, n;
assert(sln);
fields_destroy(sln->fields);
n = solution_num_projects(sln);
for (i = 0; i < n; ++i)
{
Project prj = solution_get_project(sln, i);
project_destroy(prj);
}
array_destroy(sln->projects);
free(sln);
}
/**
* Add a project to a solution.
* \param sln The solution to contain the project.
* \param prj The project to add.
*/
void solution_add_project(Solution sln, Project prj)
{
assert(sln);
assert(prj);
array_add(sln->projects, prj);
}
/**
* Get the base directory for the solution; any properties containing relative
* paths are relative to this location.
@ -122,6 +149,23 @@ const char* solution_get_name(Solution sln)
}
/**
* Retrieve a project from the solution.
* \param sln The solution to query.
* \param index The index of the project to retreive.
* \returns The project at the given index within the solution.
*/
Project solution_get_project(Solution sln, int index)
{
Project prj;
assert(sln);
prj = (Project)array_item(sln->projects, index);
return prj;
}
/**
* Retrieve a string (single value) fields from a solution, using the field indices.
* \param sln The solution object to query.
@ -135,6 +179,18 @@ const char* solution_get_value(Solution sln, enum SolutionField field)
}
/**
* Return the number of projects contained by this solution.
* \param sln The solution to query.
* \returns The number of projects contained by the solution.
*/
int solution_num_projects(Solution sln)
{
assert(sln);
return array_size(sln->projects);
}
/**
* Set the base directory of the solution.
* \param sln The solution object to modify.

View File

@ -7,10 +7,11 @@
#define PREMAKE_SOLUTION_H
#include "fields.h"
#include "project.h"
/**
* Solution value index.
* Solution field index.
* \note If you modify this list, you must also update SolutionFieldInfo[].
*/
enum SolutionField
@ -29,11 +30,14 @@ DECLARE_CLASS(Solution)
Solution solution_create(void);
void solution_destroy(Solution sln);
void solution_add_project(Solution sln, Project prj);
const char* solution_get_base_dir(Solution sln);
const char* solution_get_filename(Solution sln, const char* basename, const char* ext);
const char* solution_get_location(Solution sln);
const char* solution_get_name(Solution sln);
Project solution_get_project(Solution sln, int index);
const char* solution_get_value(Solution sln, enum SolutionField field);
int solution_num_projects(Solution sln);
void solution_set_base_dir(Solution sln, const char* base_dir);
void solution_set_location(Solution sln, const char* location);
void solution_set_name(Solution sln, const char* name);

View File

@ -21,3 +21,125 @@ int project_tests()
if (status == OKAY) status = tests_run_suite("project");
return status;
}
struct FxProject
{
Project prj;
FxProject()
{
prj = project_create();
}
~FxProject()
{
if (prj)
project_destroy(prj);
}
};
SUITE(project)
{
TEST_FIXTURE(FxProject, Create_ReturnsObject_OnSuccess)
{
CHECK(prj != NULL);
}
/**********************************************************************
* Name tests
**********************************************************************/
TEST_FIXTURE(FxProject, GetName_ReturnsNull_OnStartup)
{
const char* name = project_get_name(prj);
CHECK(name == NULL);
}
TEST_FIXTURE(FxProject, SetName_CanRoundtrip)
{
project_set_name(prj, "MyProject");
const char* name = project_get_name(prj);
CHECK_EQUAL("MyProject", name);
}
/**********************************************************************
* Base directory tests
**********************************************************************/
TEST_FIXTURE(FxProject, GetBaseDir_ReturnsNull_OnStartup)
{
const char* result = project_get_base_dir(prj);
CHECK(result == NULL);
}
TEST_FIXTURE(FxProject, SetBaseDir_CanRoundtrip)
{
project_set_base_dir(prj, "BaseDir");
const char* result = project_get_base_dir(prj);
CHECK_EQUAL("BaseDir", result);
}
/**********************************************************************
* GUID testrs
**********************************************************************/
TEST_FIXTURE(FxProject, GetGuid_ReturnsNull_OnStartup)
{
const char* result = project_get_guid(prj);
CHECK(result == NULL);
}
TEST_FIXTURE(FxProject, SetGuid_CanRoundtrip)
{
project_set_guid(prj, "AE2461B7-236F-4278-81D3-F0D476F9A4C0");
const char* result = project_get_guid(prj);
CHECK_EQUAL("AE2461B7-236F-4278-81D3-F0D476F9A4C0", result);
}
/**********************************************************************
* Location tests
**********************************************************************/
TEST_FIXTURE(FxProject, GetLocation_ReturnsNull_OnStartup)
{
const char* result = project_get_location(prj);
CHECK(result == NULL);
}
TEST_FIXTURE(FxProject, SetLocation_CanRoundtrip)
{
project_set_location(prj, "Location");
const char* result = project_get_location(prj);
CHECK_EQUAL("Location", result);
}
/**********************************************************************
* Filename tests
**********************************************************************/
TEST_FIXTURE(FxProject, GetFilename_ReturnsFullPath_OnNoLocation)
{
project_set_name(prj, "MyProject");
project_set_base_dir(prj, "/BaseDir");
const char* filename = project_get_filename(prj, NULL, ".xyz");
CHECK_EQUAL("/BaseDir/MyProject.xyz", filename);
}
TEST_FIXTURE(FxProject, GetFilename_ReturnsFullPath_OnLocation)
{
project_set_name(prj, "MyProject");
project_set_base_dir(prj, "/BaseDir");
project_set_location(prj, "Location");
const char* filename = project_get_filename(prj, NULL, ".xyz");
CHECK_EQUAL("/BaseDir/Location/MyProject.xyz", filename);
}
}

View File

@ -8,6 +8,7 @@
#include "testing/testing.h"
extern "C" {
#include "project/solution.h"
#include "project/project.h"
}
struct FxSolution
@ -109,4 +110,31 @@ SUITE(project)
const char* filename = solution_get_filename(sln, NULL, ".xyz");
CHECK_EQUAL("/BaseDir/Location/MySolution.xyz", filename);
}
/**********************************************************************
* Project containment tests
**********************************************************************/
TEST_FIXTURE(FxSolution, NumProjects_IsZero_OnStartup)
{
int result = solution_num_projects(sln);
CHECK(result == 0);
}
TEST_FIXTURE(FxSolution, AddProject_IncrementsNumProjects)
{
Project prj = project_create();
solution_add_project(sln, prj);
int result = solution_num_projects(sln);
CHECK(result == 1);
}
TEST_FIXTURE(FxSolution, AddProject_CanRoundtrip)
{
Project prj = project_create();
solution_add_project(sln, prj);
Project result = solution_get_project(sln, 0);
CHECK(prj == result);
}
}