Completed implementation of location function and project-relative pathing (r479:485)
This commit is contained in:
parent
bcc678cb91
commit
95c23ec10b
@ -63,44 +63,47 @@ const char* make_get_obj_filename(const char* filename)
|
||||
const char* make_get_project_makefile(Project prj)
|
||||
{
|
||||
const char* my_path;
|
||||
const char* their_path;
|
||||
int si, sn;
|
||||
int si, sn, in_conflict = 0;
|
||||
|
||||
Session sess = project_get_session(prj);
|
||||
|
||||
/* get the full makefile path for this project */
|
||||
my_path = project_get_filename(prj, "Makefile", NULL);
|
||||
/* get the default filename for this project */
|
||||
my_path = project_get_filename(prj, "Makefile", "");
|
||||
|
||||
/* see if any other solution wants to use this same path */
|
||||
sn = session_num_solutions(sess);
|
||||
for (si = 0; si < sn; ++si)
|
||||
for (si = 0; si < sn && !in_conflict; ++si)
|
||||
{
|
||||
const char* their_path;
|
||||
int pi, pn;
|
||||
|
||||
Solution sln2 = session_get_solution(sess, si);
|
||||
their_path = solution_get_filename(sln2, "Makefile", NULL);
|
||||
Solution sln = session_get_solution(sess, si);
|
||||
their_path = solution_get_filename(sln, "Makefile", "");
|
||||
if (cstr_eq(my_path, their_path))
|
||||
{
|
||||
/* conflict; use the alternate name */
|
||||
my_path = project_get_filename(prj, NULL, ".make");
|
||||
return my_path;
|
||||
in_conflict = 1;
|
||||
}
|
||||
|
||||
/* check any projects contained by this solution */
|
||||
pn = solution_num_projects(sln2);
|
||||
for (pi = 0; pi < pn; ++pi)
|
||||
pn = solution_num_projects(sln);
|
||||
for (pi = 0; pi < pn && !in_conflict; ++pi)
|
||||
{
|
||||
Project prj2 = solution_get_project(sln2, pi);
|
||||
Project prj2 = solution_get_project(sln, pi);
|
||||
if (prj != prj2)
|
||||
{
|
||||
their_path = project_get_filename(prj2, "Makefile", NULL);
|
||||
their_path = project_get_filename(prj2, "Makefile", "");
|
||||
if (cstr_eq(my_path, their_path))
|
||||
{
|
||||
/* conflict; use the alternate name */
|
||||
in_conflict = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if a conflict was detected use an alternate name */
|
||||
if (in_conflict)
|
||||
{
|
||||
my_path = project_get_filename(prj, NULL, ".make");
|
||||
return my_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* all good */
|
||||
@ -142,12 +145,12 @@ Strings make_get_project_names(Solution sln)
|
||||
const char* make_get_solution_makefile(Solution sln)
|
||||
{
|
||||
const char* my_path;
|
||||
const char* their_path;
|
||||
int i, n;
|
||||
|
||||
Session sess = solution_get_session(sln);
|
||||
|
||||
/* get the full makefile path for this solution */
|
||||
my_path = solution_get_filename(sln, "Makefile", NULL);
|
||||
/* get the default file name for this solution */
|
||||
my_path = solution_get_filename(sln, "Makefile", "");
|
||||
|
||||
/* see if any other solution wants to use this same path */
|
||||
n = session_num_solutions(sess);
|
||||
@ -156,7 +159,7 @@ const char* make_get_solution_makefile(Solution sln)
|
||||
Solution them = session_get_solution(sess, i);
|
||||
if (them != sln)
|
||||
{
|
||||
their_path = solution_get_filename(them, "Makefile", NULL);
|
||||
const char* their_path = solution_get_filename(them, "Makefile", "");
|
||||
if (cstr_eq(my_path, their_path))
|
||||
{
|
||||
/* conflict; use the alternate name */
|
||||
|
@ -111,13 +111,15 @@ const char* make_solution_project_rule(Solution sln, Project prj)
|
||||
char* buffer = buffers_next();
|
||||
|
||||
/* project file paths are specified relative to the solution */
|
||||
const char* sln_path = path_directory(solution_get_filename(sln, NULL, NULL));
|
||||
const char* sln_file_dir = solution_get_location(sln);
|
||||
|
||||
const char* prj_file = make_get_project_makefile(prj);
|
||||
const char* prj_file_dir = path_directory(prj_file);
|
||||
const char* prj_file_name = path_filename(prj_file);
|
||||
prj_file_dir = path_relative(sln_path, prj_file_dir);
|
||||
|
||||
prj_file_dir = path_relative(sln_file_dir, prj_file_dir);
|
||||
|
||||
/* build the rule */
|
||||
strcpy(buffer, "\t@$(MAKE)");
|
||||
if (!cstr_eq(".", prj_file_dir))
|
||||
{
|
||||
|
@ -83,7 +83,7 @@ SUITE(action)
|
||||
|
||||
TEST_FIXTURE(FxAction, Make_ProjectEntry_InSameDirectory)
|
||||
{
|
||||
project_set_location(prj, "");
|
||||
project_set_location(prj, solution_get_location(sln));
|
||||
make_solution_projects(sln, strm);
|
||||
CHECK_EQUAL(
|
||||
"My\\ Project:\n"
|
||||
@ -95,7 +95,7 @@ SUITE(action)
|
||||
|
||||
TEST_FIXTURE(FxAction, Make_ProjectEntry_InDifferentDirectory)
|
||||
{
|
||||
project_set_location(prj, "My Project");
|
||||
project_set_location(prj, "Root Folder/Solution Folder/My Project");
|
||||
make_solution_projects(sln, strm);
|
||||
CHECK_EQUAL(
|
||||
"My\\ Project:\n"
|
||||
@ -112,7 +112,7 @@ SUITE(action)
|
||||
|
||||
TEST_FIXTURE(FxAction, Gmake_CleanRule_IsCorrect)
|
||||
{
|
||||
project_set_location(prj, "");
|
||||
project_set_location(prj, solution_get_location(sln));
|
||||
make_solution_clean_rule(sln, strm);
|
||||
CHECK_EQUAL(
|
||||
"clean:\n"
|
||||
|
@ -40,7 +40,7 @@ struct FxMake
|
||||
Solution sln = solution_create();
|
||||
session_add_solution(sess, sln);
|
||||
solution_set_name(sln, name);
|
||||
solution_set_base_dir(sln, ".");
|
||||
solution_set_location(sln, ".");
|
||||
return sln;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ struct FxMake
|
||||
Project prj = project_create();
|
||||
solution_add_project(sln1, prj);
|
||||
project_set_name(prj, name);
|
||||
project_set_base_dir(prj, ".");
|
||||
project_set_location(prj, ".");
|
||||
return prj;
|
||||
}
|
||||
};
|
||||
@ -63,7 +63,7 @@ SUITE(action)
|
||||
|
||||
TEST_FIXTURE(FxMake, GetSolutionMakefile_ReturnsMakefile_OnUniqueLocation)
|
||||
{
|
||||
solution_set_location(sln1, "MySolution");
|
||||
solution_set_location(sln1, "./MySolution");
|
||||
const char* result = make_get_solution_makefile(sln1);
|
||||
CHECK_EQUAL("./MySolution/Makefile", result);
|
||||
}
|
||||
@ -76,22 +76,22 @@ SUITE(action)
|
||||
|
||||
TEST_FIXTURE(FxMake, GetProjectMakefile_ReturnsMakefile_OnUniqueLocation)
|
||||
{
|
||||
project_set_location(prj1, "MyProject");
|
||||
project_set_location(prj1, "./MyProject");
|
||||
const char* result = make_get_project_makefile(prj1);
|
||||
CHECK_EQUAL("./MyProject/Makefile", result);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxMake, GetProjectMakefile_ReturnsDotMake_OnSharedWithSolution)
|
||||
{
|
||||
project_set_location(prj2, "MyProject");
|
||||
project_set_location(prj2, "./MyProject");
|
||||
const char* result = make_get_project_makefile(prj1);
|
||||
CHECK_EQUAL("./MyProject1.make", result);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxMake, GetProjectMakefile_ReturnsDotMake_OnSharedWithProject)
|
||||
{
|
||||
project_set_location(prj1, "MyProject");
|
||||
project_set_location(prj2, "MyProject");
|
||||
project_set_location(prj1, "./MyProject");
|
||||
project_set_location(prj2, "./MyProject");
|
||||
const char* result = make_get_project_makefile(prj1);
|
||||
CHECK_EQUAL("./MyProject/MyProject1.make", result);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ struct FxAction
|
||||
session_add_solution(sess, sln);
|
||||
solution_set_name(sln, "My Solution");
|
||||
solution_set_base_dir(sln, "Root Folder");
|
||||
solution_set_location(sln, "Root Folder/Solution Folder");
|
||||
solution_add_config(sln, "Debug DLL");
|
||||
solution_add_config(sln, "Release DLL");
|
||||
|
||||
@ -36,7 +37,7 @@ struct FxAction
|
||||
solution_add_project(sln, prj);
|
||||
project_set_name(prj, "My Project");
|
||||
project_set_base_dir(prj, "Root Folder");
|
||||
project_set_location(prj, "Project Folder");
|
||||
project_set_location(prj, "Root Folder/Project Folder");
|
||||
project_set_guid(prj, "AE2461B7-236F-4278-81D3-F0D476F9A4C0");
|
||||
project_set_language(prj, "c++");
|
||||
project_set_config(prj, "Debug DLL");
|
||||
|
@ -34,7 +34,7 @@ SUITE(action)
|
||||
{
|
||||
vs2002_solution_projects(sln, strm);
|
||||
CHECK_EQUAL(
|
||||
"Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"My Project\", \"Project Folder\\My Project.vcproj\", \"{AE2461B7-236F-4278-81D3-F0D476F9A4C0}\"\n"
|
||||
"Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"My Project\", \"..\\Project Folder\\My Project.vcproj\", \"{AE2461B7-236F-4278-81D3-F0D476F9A4C0}\"\n"
|
||||
"EndProject\n",
|
||||
buffer);
|
||||
}
|
||||
|
@ -96,12 +96,7 @@ int vs2002_solution_project_configuration(Solution sln, Stream strm)
|
||||
*/
|
||||
int vs2002_solution_projects(Solution sln, Stream strm)
|
||||
{
|
||||
const char* sln_path;
|
||||
int i, n, z = OKAY;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
@ -110,13 +105,10 @@ int vs2002_solution_projects(Solution sln, Stream strm)
|
||||
const char* prj_id = project_get_guid(prj);
|
||||
const char* prj_lang = project_get_language(prj);
|
||||
const char* prj_ext = vs200x_project_file_extension(prj);
|
||||
const char* prj_file = project_get_filename(prj, prj_name, prj_ext);
|
||||
const char* prj_file = project_get_filename_relative(prj, prj_name, prj_ext);
|
||||
const char* tool_id = vs200x_tool_guid(prj_lang);
|
||||
|
||||
/* convert absolute project file name to be relative to solution */
|
||||
prj_file = path_relative(sln_path, prj_file);
|
||||
prj_file = path_translate(prj_file, "\\");
|
||||
|
||||
z |= stream_writeline(strm, "Project(\"{%s}\") = \"%s\", \"%s\", \"{%s}\"", tool_id, prj_name, prj_file, prj_id);
|
||||
z |= stream_writeline(strm, "EndProject");
|
||||
}
|
||||
|
@ -236,7 +236,6 @@ char* path_join(const char* leading, const char* trailing)
|
||||
|
||||
/* treat nulls like empty paths */
|
||||
leading = (leading != NULL) ? leading : "";
|
||||
trailing = (trailing != NULL) ? trailing : "";
|
||||
|
||||
if (!trailing)
|
||||
{
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "premake.h"
|
||||
#include "strings.h"
|
||||
#include "base/array.h"
|
||||
#include "base/string.h"
|
||||
|
||||
|
||||
DEFINE_CLASS(Strings)
|
||||
{
|
||||
@ -50,6 +52,13 @@ Strings strings_create_from_array(const char* items[])
|
||||
*/
|
||||
void strings_destroy(Strings strs)
|
||||
{
|
||||
int i, n;
|
||||
n = strings_size(strs);
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
String item = (String)array_item(strs->contents, i);
|
||||
string_destroy(item);
|
||||
}
|
||||
array_destroy(strs->contents);
|
||||
free(strs);
|
||||
}
|
||||
@ -62,7 +71,8 @@ void strings_destroy(Strings strs)
|
||||
*/
|
||||
void strings_add(Strings strs, const char* item)
|
||||
{
|
||||
array_add(strs->contents, (void*)item);
|
||||
String str = string_create(item);
|
||||
array_add(strs->contents, (void*)str);
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +83,13 @@ void strings_add(Strings strs, const char* item)
|
||||
*/
|
||||
void strings_append(Strings dest, Strings src)
|
||||
{
|
||||
array_append(dest->contents, src->contents);
|
||||
int i, n;
|
||||
n = strings_size(src);
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
const char* item = strings_item(src, i);
|
||||
strings_add(dest, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -85,7 +101,8 @@ void strings_append(Strings dest, Strings src)
|
||||
*/
|
||||
const char* strings_item(Strings strs, int index)
|
||||
{
|
||||
return (const char*)array_item(strs->contents, index);
|
||||
String item = (String)array_item(strs->contents, index);
|
||||
return string_cstr(item);
|
||||
}
|
||||
|
||||
|
||||
@ -97,7 +114,11 @@ const char* strings_item(Strings strs, int index)
|
||||
*/
|
||||
void strings_set(Strings strs, int index, const char* item)
|
||||
{
|
||||
array_set(strs->contents, index, (void*)item);
|
||||
String str = (String)array_item(strs->contents, index);
|
||||
string_destroy(str);
|
||||
|
||||
str = string_create(item);
|
||||
array_set(strs->contents, index, (void*)str);
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,10 +12,10 @@
|
||||
#include "base/env.h"
|
||||
|
||||
|
||||
struct FieldInfo BlockFieldInfo[] =
|
||||
FieldInfo BlockFieldInfo[] =
|
||||
{
|
||||
{ "defines", ListField, NULL },
|
||||
{ "objdir", StringField, NULL },
|
||||
{ "objdir", PathField, NULL },
|
||||
{ "terms", ListField, NULL },
|
||||
{ 0, 0, NULL }
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ enum BlockField
|
||||
NumBlockFields
|
||||
};
|
||||
|
||||
extern struct FieldInfo BlockFieldInfo[];
|
||||
extern FieldInfo BlockFieldInfo[];
|
||||
|
||||
|
||||
DECLARE_CLASS(Block)
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
DEFINE_CLASS(Fields)
|
||||
{
|
||||
FieldInfo* info;
|
||||
Strings* values;
|
||||
int count;
|
||||
};
|
||||
@ -23,7 +24,7 @@ DEFINE_CLASS(Fields)
|
||||
* \param info Metadata about the field collection.
|
||||
* \returns A new collection of fields.
|
||||
*/
|
||||
Fields fields_create(struct FieldInfo* info)
|
||||
Fields fields_create(FieldInfo* info)
|
||||
{
|
||||
int i;
|
||||
Fields fields;
|
||||
@ -31,6 +32,7 @@ Fields fields_create(struct FieldInfo* info)
|
||||
assert(info);
|
||||
|
||||
fields = ALLOC_CLASS(Fields);
|
||||
fields->info = info;
|
||||
|
||||
/* figure out how many fields are in the collection */
|
||||
for (i = 0; info[i].name != NULL; ++i);
|
||||
@ -76,6 +78,17 @@ void fields_add_value(Fields fields, int index, const char* value)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Identify the type of field at the given index.
|
||||
*/
|
||||
FieldKind fields_get_kind(Fields fields, int index)
|
||||
{
|
||||
assert(fields);
|
||||
assert(index >= 0 && index < fields->count);
|
||||
return fields->info[index].kind;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the value of a string (single value) field.
|
||||
* \returns The field value if set, or NULL.
|
||||
@ -149,3 +162,13 @@ void fields_set_values(Fields fields, int index, Strings values)
|
||||
|
||||
fields->values[index] = values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of fields in the collection.
|
||||
*/
|
||||
int fields_size(Fields fields)
|
||||
{
|
||||
assert(fields);
|
||||
return fields->count;
|
||||
}
|
||||
|
@ -20,12 +20,13 @@
|
||||
/**
|
||||
* Field types.
|
||||
*/
|
||||
enum FieldKind
|
||||
typedef enum enum_FieldKind
|
||||
{
|
||||
StringField,
|
||||
ListField,
|
||||
FilesField
|
||||
};
|
||||
StringField, /**< field containing a single, string value */
|
||||
ListField, /**< field containing a list of string values */
|
||||
FilesField, /**< field containing a list of file names */
|
||||
PathField /**< field containing a file path (directory or file name) */
|
||||
} FieldKind;
|
||||
|
||||
|
||||
/**
|
||||
@ -39,25 +40,27 @@ typedef int (*FieldValidator)(const char* value);
|
||||
/**
|
||||
* Metadata about a project object field.
|
||||
*/
|
||||
struct FieldInfo
|
||||
typedef struct struct_FieldInfo
|
||||
{
|
||||
const char* name; /**< The name of the field. */
|
||||
enum FieldKind kind; /**< StringField, ListField, etc. */
|
||||
FieldKind kind; /**< StringField, ListField, etc. */
|
||||
FieldValidator validator; /**< The field validation function */
|
||||
};
|
||||
} FieldInfo;
|
||||
|
||||
|
||||
DECLARE_CLASS(Fields)
|
||||
|
||||
|
||||
Fields fields_create(struct FieldInfo* info);
|
||||
Fields fields_create(FieldInfo* info);
|
||||
void fields_destroy(Fields fields);
|
||||
|
||||
void fields_add_value(Fields fields, int index, const char* value);
|
||||
FieldKind fields_get_kind(Fields fields, int index);
|
||||
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);
|
||||
int fields_size(Fields fields);
|
||||
|
||||
#endif
|
||||
/* @} */
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "base/strings.h"
|
||||
|
||||
|
||||
struct FieldInfo ProjectFieldInfo[] =
|
||||
FieldInfo ProjectFieldInfo[] =
|
||||
{
|
||||
{ "basedir", StringField, NULL },
|
||||
{ "files", FilesField, NULL },
|
||||
@ -87,7 +87,7 @@ void project_destroy(Project prj)
|
||||
*/
|
||||
const char* project_get_base_dir(Project prj)
|
||||
{
|
||||
return project_get_value(prj, ProjectBaseDirectory);
|
||||
return project_get_value(prj, ProjectBaseDir);
|
||||
}
|
||||
|
||||
|
||||
@ -157,7 +157,6 @@ Strings project_get_config_values(Project prj, enum BlockField field)
|
||||
|
||||
project_get_values_from_blocks(prj, values, solution_get_blocks(prj->solution), field);
|
||||
project_get_values_from_blocks(prj, values, project_get_blocks(prj), field);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@ -183,7 +182,7 @@ Fields project_get_fields(Project prj)
|
||||
|
||||
|
||||
/**
|
||||
* Get the path to the project output file, using the provided file extension.
|
||||
* Get the path to the project output file, using the provided file name and 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.
|
||||
@ -191,33 +190,38 @@ Fields project_get_fields(Project prj)
|
||||
*/
|
||||
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);
|
||||
|
||||
const char* location = project_get_location(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;
|
||||
return path_assemble(location, basename, ext);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relative path from the solution to the project file, using the
|
||||
* provided file name and 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_relative(Project prj, const char* basename, const char* ext)
|
||||
{
|
||||
const char* sln_location;
|
||||
const char* abs_filename;
|
||||
assert(prj);
|
||||
assert(prj->solution);
|
||||
|
||||
sln_location = solution_get_location(prj->solution);
|
||||
abs_filename = project_get_filename(prj, basename, ext);
|
||||
|
||||
return path_relative(sln_location, abs_filename);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the list of source files associated with a project.
|
||||
*/
|
||||
@ -334,7 +338,7 @@ int project_is_valid_language(const char* language)
|
||||
*/
|
||||
void project_set_base_dir(Project prj, const char* base_dir)
|
||||
{
|
||||
project_set_value(prj, ProjectBaseDirectory, base_dir);
|
||||
project_set_value(prj, ProjectBaseDir, base_dir);
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,7 +24,7 @@ DECLARE_CLASS(Project)
|
||||
*/
|
||||
enum ProjectField
|
||||
{
|
||||
ProjectBaseDirectory,
|
||||
ProjectBaseDir,
|
||||
ProjectFiles,
|
||||
ProjectGuid,
|
||||
ProjectLanguage,
|
||||
@ -33,7 +33,7 @@ enum ProjectField
|
||||
NumProjectFields
|
||||
};
|
||||
|
||||
extern struct FieldInfo ProjectFieldInfo[];
|
||||
extern FieldInfo ProjectFieldInfo[];
|
||||
|
||||
|
||||
Project project_create(void);
|
||||
@ -45,6 +45,7 @@ const char* project_get_config(Project prj);
|
||||
Strings project_get_config_values(Project prj, enum BlockField field);
|
||||
Fields project_get_fields(Project prj);
|
||||
const char* project_get_filename(Project prj, const char* basename, const char* ext);
|
||||
const char* project_get_filename_relative(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);
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "base/strings.h"
|
||||
|
||||
|
||||
struct FieldInfo SolutionFieldInfo[] =
|
||||
FieldInfo SolutionFieldInfo[] =
|
||||
{
|
||||
{ "basedir", StringField, NULL },
|
||||
{ "configurations", ListField, NULL },
|
||||
@ -101,7 +101,7 @@ void solution_add_project(Solution sln, Project prj)
|
||||
*/
|
||||
const char* solution_get_base_dir(Solution sln)
|
||||
{
|
||||
return solution_get_value(sln, SolutionBaseDirectory);
|
||||
return solution_get_value(sln, SolutionBaseDir);
|
||||
}
|
||||
|
||||
|
||||
@ -158,29 +158,12 @@ Fields solution_get_fields(Solution sln)
|
||||
*/
|
||||
const char* solution_get_filename(Solution sln, const char* basename, const char* ext)
|
||||
{
|
||||
const char* base_dir;
|
||||
const char* location;
|
||||
const char* directory;
|
||||
const char* result;
|
||||
|
||||
assert(sln);
|
||||
|
||||
const char* location = solution_get_location(sln);
|
||||
if (!basename)
|
||||
{
|
||||
basename = solution_get_name(sln);
|
||||
}
|
||||
|
||||
if (!ext)
|
||||
{
|
||||
ext = "";
|
||||
}
|
||||
|
||||
base_dir = solution_get_base_dir(sln);
|
||||
location = solution_get_location(sln);
|
||||
directory = path_join(base_dir, location);
|
||||
|
||||
result = path_assemble(directory, basename, ext);
|
||||
return result;
|
||||
return path_assemble(location, basename, ext);
|
||||
}
|
||||
|
||||
|
||||
@ -273,7 +256,7 @@ int solution_num_projects(Solution sln)
|
||||
*/
|
||||
void solution_set_base_dir(Solution sln, const char* base_dir)
|
||||
{
|
||||
solution_set_value(sln, SolutionBaseDirectory, base_dir);
|
||||
solution_set_value(sln, SolutionBaseDir, base_dir);
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ DECLARE_CLASS(Solution)
|
||||
*/
|
||||
enum SolutionField
|
||||
{
|
||||
SolutionBaseDirectory,
|
||||
SolutionBaseDir,
|
||||
SolutionConfigurations,
|
||||
SolutionLanguage,
|
||||
SolutionLocation,
|
||||
@ -33,7 +33,7 @@ enum SolutionField
|
||||
NumSolutionFields
|
||||
};
|
||||
|
||||
extern struct FieldInfo SolutionFieldInfo[];
|
||||
extern FieldInfo SolutionFieldInfo[];
|
||||
|
||||
|
||||
Solution solution_create(void);
|
||||
|
@ -21,12 +21,16 @@ struct FxProject
|
||||
FxProject()
|
||||
{
|
||||
sln = solution_create();
|
||||
solution_set_name(sln, "MySolution");
|
||||
solution_set_location(sln, "/BaseDir/MySolution");
|
||||
|
||||
prj = project_create();
|
||||
solution_add_project(sln, prj);
|
||||
project_set_name(prj, "MyProject");
|
||||
}
|
||||
|
||||
~FxProject()
|
||||
{
|
||||
project_destroy(prj);
|
||||
solution_destroy(sln);
|
||||
}
|
||||
};
|
||||
@ -44,21 +48,18 @@ SUITE(project)
|
||||
* Filename tests
|
||||
**********************************************************************/
|
||||
|
||||
TEST_FIXTURE(FxProject, GetFilename_ReturnsFullPath_OnNoLocation)
|
||||
TEST_FIXTURE(FxProject, GetFilenameRel_ReturnsCorrectPath_OnSameDir)
|
||||
{
|
||||
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);
|
||||
project_set_location(prj, "/BaseDir/MySolution");
|
||||
const char* filename = project_get_filename_relative(prj, NULL, ".xyz");
|
||||
CHECK_EQUAL("MyProject.xyz", filename);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxProject, GetFilename_ReturnsFullPath_OnLocation)
|
||||
TEST_FIXTURE(FxProject, GetFilenameRel_ReturnsCorrectPath_OnDifferentDir)
|
||||
{
|
||||
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);
|
||||
project_set_location(prj, "/BaseDir/MyProject");
|
||||
const char* filename = project_get_filename_relative(prj, NULL, ".xyz");
|
||||
CHECK_EQUAL("../MyProject/MyProject.xyz", filename);
|
||||
}
|
||||
|
||||
|
||||
@ -101,15 +102,8 @@ SUITE(project)
|
||||
* Solution tests
|
||||
**********************************************************************/
|
||||
|
||||
TEST_FIXTURE(FxProject, GetSolution_ReturnsNull_OnStartup)
|
||||
{
|
||||
Solution result = project_get_solution(prj);
|
||||
CHECK(result == NULL);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxProject, SetSolution_CanRoundtrip)
|
||||
{
|
||||
project_set_solution(prj, sln);
|
||||
CHECK(sln == project_get_solution(prj));
|
||||
}
|
||||
|
||||
|
@ -75,28 +75,6 @@ SUITE(project)
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Filename tests
|
||||
**********************************************************************/
|
||||
|
||||
TEST_FIXTURE(FxSolution, GetFilename_ReturnsFullPath_OnNoLocation)
|
||||
{
|
||||
solution_set_name(sln, "MySolution");
|
||||
solution_set_base_dir(sln, "/BaseDir");
|
||||
const char* filename = solution_get_filename(sln, NULL, ".xyz");
|
||||
CHECK_EQUAL("/BaseDir/MySolution.xyz", filename);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxSolution, GetFilename_ReturnsFullPath_OnLocation)
|
||||
{
|
||||
solution_set_name(sln, "MySolution");
|
||||
solution_set_base_dir(sln, "/BaseDir");
|
||||
solution_set_location(sln, "Location");
|
||||
const char* filename = solution_get_filename(sln, NULL, ".xyz");
|
||||
CHECK_EQUAL("/BaseDir/Location/MySolution.xyz", filename);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Project containment tests
|
||||
**********************************************************************/
|
||||
|
@ -10,10 +10,10 @@
|
||||
#include "base/error.h"
|
||||
|
||||
|
||||
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 void fn_accessor_append_value(lua_State* L, struct FieldInfo* field, int tbl, int idx);
|
||||
static int fn_accessor_object_has_field(FieldInfo* fields, const char* field_name);
|
||||
static int fn_accessor_register(lua_State* L, FieldInfo* fields);
|
||||
static int fn_accessor_register_field(lua_State* L, FieldInfo* field);
|
||||
static void fn_accessor_append_value(lua_State* L, FieldInfo* field, int tbl, int idx);
|
||||
|
||||
|
||||
/**
|
||||
@ -37,7 +37,7 @@ int fn_accessor_register_all(lua_State* L)
|
||||
* Register the accessor functions for a particular set of fields.
|
||||
* \returns OKAY if successful.
|
||||
*/
|
||||
static int fn_accessor_register(lua_State* L, struct FieldInfo* fields)
|
||||
static int fn_accessor_register(lua_State* L, FieldInfo* fields)
|
||||
{
|
||||
int i, z = OKAY;
|
||||
|
||||
@ -54,7 +54,7 @@ static int fn_accessor_register(lua_State* L, struct FieldInfo* fields)
|
||||
* Register a single accessor function.
|
||||
* \returns OKAY if successful.
|
||||
*/
|
||||
static int fn_accessor_register_field(lua_State* L, struct FieldInfo* field)
|
||||
static int fn_accessor_register_field(lua_State* L, FieldInfo* field)
|
||||
{
|
||||
int container_type, z;
|
||||
|
||||
@ -84,7 +84,7 @@ static int fn_accessor_register_field(lua_State* L, struct FieldInfo* field)
|
||||
* Determine if a field list contains a field with a particular name.
|
||||
* \returns True if the field is contained by the list.
|
||||
*/
|
||||
static int fn_accessor_object_has_field(struct FieldInfo* fields, const char* field_name)
|
||||
static int fn_accessor_object_has_field(FieldInfo* fields, const char* field_name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; fields[i].name != NULL; ++i)
|
||||
@ -104,7 +104,7 @@ static int fn_accessor_object_has_field(struct FieldInfo* fields, const char* fi
|
||||
*/
|
||||
int fn_accessor(lua_State* L)
|
||||
{
|
||||
struct FieldInfo* field;
|
||||
FieldInfo* field;
|
||||
int container_type;
|
||||
|
||||
/* get the required container object */
|
||||
@ -115,12 +115,12 @@ int fn_accessor(lua_State* L)
|
||||
}
|
||||
|
||||
/* get information about the field being accessed */
|
||||
field = (struct FieldInfo*)lua_touserdata(L, lua_upvalueindex(2));
|
||||
field = (FieldInfo*)lua_touserdata(L, lua_upvalueindex(2));
|
||||
|
||||
/* if a value is provided, set the field */
|
||||
if (lua_gettop(L) > 1)
|
||||
{
|
||||
if (field->kind == StringField)
|
||||
if (field->kind == StringField || field->kind == PathField)
|
||||
{
|
||||
fn_accessor_set_string_value(L, field);
|
||||
}
|
||||
@ -140,7 +140,7 @@ int fn_accessor(lua_State* L)
|
||||
* Sets a string field to the value on the bottom of the Lua stack.
|
||||
* \returns OKAY if successful.
|
||||
*/
|
||||
int fn_accessor_set_string_value(lua_State* L, struct FieldInfo* field)
|
||||
int fn_accessor_set_string_value(lua_State* L, FieldInfo* field)
|
||||
{
|
||||
/* can't set lists to simple fields */
|
||||
if (lua_istable(L, 1))
|
||||
@ -171,7 +171,7 @@ int fn_accessor_set_string_value(lua_State* L, struct FieldInfo* field)
|
||||
* Appends the value or list at the bottom of the Lua stack to the specified list field.
|
||||
* \returns OKAY if successful.
|
||||
*/
|
||||
int fn_accessor_set_list_value(lua_State* L, struct FieldInfo* field)
|
||||
int fn_accessor_set_list_value(lua_State* L, FieldInfo* field)
|
||||
{
|
||||
/* get the current value of the field */
|
||||
lua_getfield(L, -1, field->name);
|
||||
@ -194,7 +194,7 @@ int fn_accessor_set_list_value(lua_State* L, struct FieldInfo* field)
|
||||
* \param tbl The table to contain the values.
|
||||
* \param idx The value to add to the table.
|
||||
*/
|
||||
static void fn_accessor_append_value(lua_State* L, struct FieldInfo* field, int tbl, int idx)
|
||||
static void fn_accessor_append_value(lua_State* L, FieldInfo* field, int tbl, int idx)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
*/
|
||||
int fn_configurations(lua_State* L)
|
||||
{
|
||||
struct FieldInfo* field;
|
||||
FieldInfo* field;
|
||||
|
||||
if (!script_internal_get_active_object(L, SolutionObject, IS_REQUIRED))
|
||||
{
|
||||
@ -29,7 +29,7 @@ int fn_configurations(lua_State* L)
|
||||
lua_pop(L, 1);
|
||||
|
||||
/* get information about the field being accessed */
|
||||
field = (struct FieldInfo*)lua_touserdata(L, lua_upvalueindex(2));
|
||||
field = (FieldInfo*)lua_touserdata(L, lua_upvalueindex(2));
|
||||
|
||||
/* if a value is provided, set the field */
|
||||
if (lua_gettop(L) > 1)
|
||||
|
@ -56,7 +56,7 @@ int fn_project(lua_State* L)
|
||||
|
||||
/* set the base directory */
|
||||
lua_pushstring(L, script_internal_script_dir(L));
|
||||
lua_setfield(L, -2, ProjectFieldInfo[ProjectBaseDirectory].name);
|
||||
lua_setfield(L, -2, ProjectFieldInfo[ProjectBaseDir].name);
|
||||
|
||||
/* set a default GUID */
|
||||
lua_pushstring(L, guid_create());
|
||||
|
@ -38,7 +38,7 @@ int fn_solution(lua_State* L)
|
||||
|
||||
/* set the base directory */
|
||||
lua_pushstring(L, script_internal_script_dir(L));
|
||||
lua_setfield(L, -2, SolutionFieldInfo[SolutionBaseDirectory].name);
|
||||
lua_setfield(L, -2, SolutionFieldInfo[SolutionBaseDir].name);
|
||||
|
||||
/* create an empty list of projects */
|
||||
lua_newtable(L);
|
||||
|
@ -29,7 +29,8 @@ int script_internal_create_block(lua_State* L)
|
||||
/* set all list-type configuration block values to empty tables */
|
||||
for (i = 0; i < NumBlockFields; ++i)
|
||||
{
|
||||
if (BlockFieldInfo[i].kind != StringField)
|
||||
int kind = BlockFieldInfo[i].kind;
|
||||
if (kind != StringField && kind != PathField)
|
||||
{
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, -2, BlockFieldInfo[i].name);
|
||||
@ -184,14 +185,14 @@ const char* script_internal_script_dir(lua_State* L)
|
||||
* \param L The Lua state.
|
||||
* \param fields The list of object fields.
|
||||
*/
|
||||
void script_internal_populate_object(lua_State* L, struct FieldInfo* fields)
|
||||
void script_internal_populate_object(lua_State* L, FieldInfo* fields)
|
||||
{
|
||||
struct FieldInfo* field;
|
||||
FieldInfo* field;
|
||||
|
||||
/* set all list-type configuration values to empty tables */
|
||||
for (field = fields; field->name != NULL; ++field)
|
||||
{
|
||||
if (field->kind != StringField)
|
||||
if (field->kind != StringField && field->kind != PathField)
|
||||
{
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, -2, field->name);
|
||||
|
@ -40,12 +40,12 @@ int script_internal_create_block(lua_State* L);
|
||||
int script_internal_get_active_object(lua_State* L, enum ObjectType type, int is_required);
|
||||
void script_internal_set_active_object(lua_State* L, enum ObjectType type);
|
||||
const char* script_internal_script_dir(lua_State* L);
|
||||
void script_internal_populate_object(lua_State* L, struct FieldInfo* fields);
|
||||
void script_internal_populate_object(lua_State* L, FieldInfo* fields);
|
||||
|
||||
/* Generic project object field getter/setter API */
|
||||
int fn_accessor_register_all(lua_State* L);
|
||||
int fn_accessor_set_string_value(lua_State* L, struct FieldInfo* field);
|
||||
int fn_accessor_set_list_value(lua_State* L, struct FieldInfo* field);
|
||||
int fn_accessor_set_string_value(lua_State* L, FieldInfo* field);
|
||||
int fn_accessor_set_list_value(lua_State* L, FieldInfo* field);
|
||||
|
||||
/* script function handlers */
|
||||
int fn_accessor(lua_State* L);
|
||||
|
@ -28,6 +28,7 @@ struct FxUnloadProject
|
||||
"solution('MySolution');"
|
||||
" configurations {'Debug','Release'};"
|
||||
"prj = project('MyProject');"
|
||||
" prj.basedir = '/basedir';"
|
||||
" guid '0C202E43-B9AF-4972-822B-5A42F0BF008C';"
|
||||
" language 'c++';"
|
||||
" files { 'Hello.cpp', 'Goodbye.cpp' };"
|
||||
@ -55,7 +56,7 @@ SUITE(unload)
|
||||
{
|
||||
unload_project(L, prj);
|
||||
const char* result = project_get_base_dir(prj);
|
||||
CHECK_EQUAL("(string)", result);
|
||||
CHECK_EQUAL("/basedir", result);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxUnloadProject, UnloadProject_UnloadsFiles)
|
||||
@ -69,6 +70,18 @@ SUITE(unload)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxUnloadProject, UnloadProject_RepointsFiles_OnLocation)
|
||||
{
|
||||
script_run_string(script, "location 'build'; return prj");
|
||||
unload_project(L, prj);
|
||||
Strings files = project_get_files(prj);
|
||||
CHECK(strings_size(files) == 2);
|
||||
if (strings_size(files) == 2) {
|
||||
CHECK_EQUAL("../Hello.cpp", strings_item(files, 0));
|
||||
CHECK_EQUAL("../Goodbye.cpp", strings_item(files, 1));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxUnloadProject, UnloadProject_SetsGuid)
|
||||
{
|
||||
unload_project(L, prj);
|
||||
@ -82,5 +95,20 @@ SUITE(unload)
|
||||
const char* result = project_get_language(prj);
|
||||
CHECK_EQUAL("c++", result);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxUnloadProject, UnloadProject_SetsLocation_OnUnsetLocation)
|
||||
{
|
||||
unload_project(L, prj);
|
||||
const char* result = project_get_location(prj);
|
||||
CHECK_EQUAL("/basedir", result);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxUnloadProject, UnloadProject_SetsLocation_OnSetLocation)
|
||||
{
|
||||
script_run_string(script, "location 'location'; return prj");
|
||||
unload_project(L, prj);
|
||||
const char* result = project_get_location(prj);
|
||||
CHECK_EQUAL("/basedir/location", result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,8 @@ struct FxUnloadSolution
|
||||
script_run_string(script,
|
||||
"sln = solution('MySolution');"
|
||||
" configurations { 'Debug', 'Release' };"
|
||||
" sln.basedir = '/basedir';"
|
||||
" location 'location';"
|
||||
"return sln");
|
||||
}
|
||||
|
||||
@ -51,7 +53,7 @@ SUITE(unload)
|
||||
{
|
||||
unload_solution(L, sln);
|
||||
const char* result = solution_get_base_dir(sln);
|
||||
CHECK_EQUAL("(string)", result);
|
||||
CHECK_EQUAL("/basedir", result);
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxUnloadSolution, UnloadSolution_SetsConfigurations)
|
||||
@ -61,5 +63,12 @@ SUITE(unload)
|
||||
CHECK_EQUAL("Debug", solution_get_config(sln, 0));
|
||||
CHECK_EQUAL("Release", solution_get_config(sln, 1));
|
||||
}
|
||||
|
||||
TEST_FIXTURE(FxUnloadSolution, UnloadSolution_SetsLocation)
|
||||
{
|
||||
unload_solution(L, sln);
|
||||
const char* result = solution_get_location(sln);
|
||||
CHECK_EQUAL("/basedir/location", result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,12 @@
|
||||
#include <assert.h>
|
||||
#include "premake.h"
|
||||
#include "script/script_internal.h"
|
||||
#include "base/path.h"
|
||||
|
||||
|
||||
static int unload_blocks(lua_State* L, struct UnloadFuncs* funcs, Blocks blocks);
|
||||
static int unload_projects(lua_State* L, struct UnloadFuncs* funcs, Solution sln);
|
||||
static int unload_repoint_paths(Fields fields, int base_dir_idx, int location_idx);
|
||||
|
||||
|
||||
/**
|
||||
@ -126,7 +128,7 @@ static int unload_blocks(lua_State* L, struct UnloadFuncs* funcs, Blocks blocks)
|
||||
}
|
||||
|
||||
|
||||
int unload_fields(lua_State* L, Fields fields, struct FieldInfo* info)
|
||||
int unload_fields(lua_State* L, Fields fields, FieldInfo* info)
|
||||
{
|
||||
const char* value;
|
||||
int fi;
|
||||
@ -179,7 +181,10 @@ int unload_fields(lua_State* L, Fields fields, struct FieldInfo* info)
|
||||
*/
|
||||
int unload_solution(lua_State* L, Solution sln)
|
||||
{
|
||||
return unload_fields(L, solution_get_fields(sln), SolutionFieldInfo);
|
||||
Fields fields = solution_get_fields(sln);
|
||||
int z = unload_fields(L, fields, SolutionFieldInfo);
|
||||
unload_repoint_paths(fields, SolutionBaseDir, SolutionLocation);
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
@ -191,7 +196,10 @@ int unload_solution(lua_State* L, Solution sln)
|
||||
*/
|
||||
int unload_project(lua_State* L, Project prj)
|
||||
{
|
||||
return unload_fields(L, project_get_fields(prj), ProjectFieldInfo);
|
||||
Fields fields = project_get_fields(prj);
|
||||
int z = unload_fields(L, fields, ProjectFieldInfo);
|
||||
unload_repoint_paths(fields, ProjectBaseDir, ProjectLocation);
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
@ -205,3 +213,52 @@ int unload_block(lua_State* L, Block blk)
|
||||
{
|
||||
return unload_fields(L, block_get_fields(blk), BlockFieldInfo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Walks a list of fields and repoints all paths to be relative to
|
||||
* base_directory/location. Once this is done, all paths will end up
|
||||
* relative to the generated project or solution file.
|
||||
* \param fields The list of fields to repoint.
|
||||
* \param base_dir_idx The index of the BaseDir field in the field list.
|
||||
* \param location_idx The index of the Location field in the field list.
|
||||
*/
|
||||
int unload_repoint_paths(Fields fields, int base_dir_idx, int location_idx)
|
||||
{
|
||||
const char* base_dir;
|
||||
const char* location;
|
||||
int fi, fn;
|
||||
|
||||
/* first I have to update the Location field; this is the absolute path that
|
||||
* I will base all the other relative paths upon */
|
||||
base_dir = fields_get_value(fields, base_dir_idx);
|
||||
location = path_join(base_dir, fields_get_value(fields, location_idx));
|
||||
fields_set_value(fields, location_idx, location);
|
||||
|
||||
/* now I can can for other pathed fields and repoint them */
|
||||
fn = fields_size(fields);
|
||||
for (fi = 0; fi < fn; ++fi)
|
||||
{
|
||||
/* only repoint pathed fields */
|
||||
int kind = fields_get_kind(fields, fi);
|
||||
if (kind == FilesField || kind == PathField)
|
||||
{
|
||||
/* enumerate all values of the field */
|
||||
int vi, vn;
|
||||
Strings values = fields_get_values(fields, fi);
|
||||
vn = strings_size(values);
|
||||
for (vi = 0; vi < vn; ++vi)
|
||||
{
|
||||
const char* value = strings_item(values, vi);
|
||||
|
||||
const char* abs_path = path_join(base_dir, value);
|
||||
const char* rel_path = path_relative(location, abs_path);
|
||||
|
||||
strings_set(values, vi, rel_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OKAY;
|
||||
}
|
||||
|
Reference in New Issue
Block a user