ENH: add install files generators for targets which have PUBLIC_HEADER,
PRIVATE_HEADER or RESOURCE_FILES property, use the destination for the public headers as include directory property for exported libraries Alex
This commit is contained in:
parent
556b1257ac
commit
3e12a6cb91
|
@ -1081,12 +1081,13 @@ void cmGlobalGenerator::AddTargetToExports(const char* exportSetName,
|
||||||
cmInstallTargetGenerator* runTime,
|
cmInstallTargetGenerator* runTime,
|
||||||
cmInstallTargetGenerator* library,
|
cmInstallTargetGenerator* library,
|
||||||
cmInstallTargetGenerator* framework,
|
cmInstallTargetGenerator* framework,
|
||||||
cmInstallTargetGenerator* bundle)
|
cmInstallTargetGenerator* bundle,
|
||||||
|
cmInstallFilesGenerator* headers)
|
||||||
{
|
{
|
||||||
if ((exportSetName) && (*exportSetName) && (target))
|
if ((exportSetName) && (*exportSetName) && (target))
|
||||||
{
|
{
|
||||||
cmTargetExport* te = new cmTargetExport(target, archive, runTime, library,
|
cmTargetExport* te = new cmTargetExport(target, archive, runTime, library,
|
||||||
framework, bundle);
|
framework, bundle, headers);
|
||||||
this->ExportSets[exportSetName].push_back(te);
|
this->ExportSets[exportSetName].push_back(te);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ class cmExternalMakefileProjectGenerator;
|
||||||
class cmTarget;
|
class cmTarget;
|
||||||
class cmTargetExport;
|
class cmTargetExport;
|
||||||
class cmInstallTargetGenerator;
|
class cmInstallTargetGenerator;
|
||||||
|
class cmInstallFilesGenerator;
|
||||||
|
|
||||||
/** \class cmGlobalGenerator
|
/** \class cmGlobalGenerator
|
||||||
* \brief Responable for overseeing the generation process for the entire tree
|
* \brief Responable for overseeing the generation process for the entire tree
|
||||||
|
@ -144,7 +145,8 @@ public:
|
||||||
cmInstallTargetGenerator* runTime,
|
cmInstallTargetGenerator* runTime,
|
||||||
cmInstallTargetGenerator* library,
|
cmInstallTargetGenerator* library,
|
||||||
cmInstallTargetGenerator* framework,
|
cmInstallTargetGenerator* framework,
|
||||||
cmInstallTargetGenerator* bundle);
|
cmInstallTargetGenerator* bundle,
|
||||||
|
cmInstallFilesGenerator* publicHeaders);
|
||||||
///! Get the export target set with the given name
|
///! Get the export target set with the given name
|
||||||
const std::vector<cmTargetExport*>* GetExportSet(const char* name) const;
|
const std::vector<cmTargetExport*>* GetExportSet(const char* name) const;
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
|
||||||
cmCAStringVector frameworkArgVector (&argHelper, "FRAMEWORK", &group);
|
cmCAStringVector frameworkArgVector (&argHelper, "FRAMEWORK", &group);
|
||||||
cmCAStringVector bundleArgVector (&argHelper, "BUNDLE", &group);
|
cmCAStringVector bundleArgVector (&argHelper, "BUNDLE", &group);
|
||||||
cmCAStringVector resourcesArgVector (&argHelper, "RESOURCE", &group);
|
cmCAStringVector resourcesArgVector (&argHelper, "RESOURCE", &group);
|
||||||
cmCAStringVector publicHeaderArgVector(&argHelper, "PUBLIC_HEADER ", &group);
|
cmCAStringVector publicHeaderArgVector(&argHelper, "PUBLIC_HEADER", &group);
|
||||||
cmCAStringVector privateHeaderArgVector(&argHelper,"PRIVATE_HEADER", &group);
|
cmCAStringVector privateHeaderArgVector(&argHelper,"PRIVATE_HEADER", &group);
|
||||||
genericArgVector.Follows(0);
|
genericArgVector.Follows(0);
|
||||||
group.Follows(&genericArgVector);
|
group.Follows(&genericArgVector);
|
||||||
|
@ -200,7 +200,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
|
||||||
resourcesArgs.Parse (&resourcesArgVector.GetVector(), &unknownArgs);
|
resourcesArgs.Parse (&resourcesArgVector.GetVector(), &unknownArgs);
|
||||||
publicHeaderArgs.Parse (&publicHeaderArgVector.GetVector(), &unknownArgs);
|
publicHeaderArgs.Parse (&publicHeaderArgVector.GetVector(), &unknownArgs);
|
||||||
privateHeaderArgs.Parse(&privateHeaderArgVector.GetVector(), &unknownArgs);
|
privateHeaderArgs.Parse(&privateHeaderArgVector.GetVector(), &unknownArgs);
|
||||||
|
|
||||||
if(!unknownArgs.empty())
|
if(!unknownArgs.empty())
|
||||||
{
|
{
|
||||||
// Unknown argument.
|
// Unknown argument.
|
||||||
|
@ -290,9 +290,9 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
|
||||||
cmInstallTargetGenerator* runtimeGenerator = 0;
|
cmInstallTargetGenerator* runtimeGenerator = 0;
|
||||||
cmInstallTargetGenerator* frameworkGenerator = 0;
|
cmInstallTargetGenerator* frameworkGenerator = 0;
|
||||||
cmInstallTargetGenerator* bundleGenerator = 0;
|
cmInstallTargetGenerator* bundleGenerator = 0;
|
||||||
cmInstallTargetGenerator* resourcesGenerator = 0;
|
cmInstallFilesGenerator* resourcesGenerator = 0;
|
||||||
cmInstallTargetGenerator* publicHeaderGenerator = 0;
|
cmInstallFilesGenerator* publicHeaderGenerator = 0;
|
||||||
cmInstallTargetGenerator* privateHeaderGenerator = 0;
|
cmInstallFilesGenerator* privateHeaderGenerator = 0;
|
||||||
|
|
||||||
switch(target.GetType())
|
switch(target.GetType())
|
||||||
{
|
{
|
||||||
|
@ -362,13 +362,6 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if(target.GetPropertyAsBool("FRAMEWORK"))
|
|
||||||
{
|
|
||||||
// Create the files install generator.
|
|
||||||
this->Makefile->AddInstallGenerator(CreateInstallFilesGenerator(
|
|
||||||
absFiles, publicHeaderArgs, false);
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case cmTarget::STATIC_LIBRARY:
|
case cmTarget::STATIC_LIBRARY:
|
||||||
|
@ -460,25 +453,106 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
|
||||||
// Ignore the case.
|
// Ignore the case.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if(target.GetProperty("ASSOCIATED_FILES");
|
||||||
|
|
||||||
|
const char* files = target.GetProperty("PUBLIC_HEADER");
|
||||||
|
if ((files) && (*files))
|
||||||
|
{
|
||||||
|
std::vector<std::string> relFiles;
|
||||||
|
cmSystemTools::ExpandListArgument(files, relFiles);
|
||||||
|
std::vector<std::string> absFiles;
|
||||||
|
if (!this->MakeFilesFullPath("PUBLIC_HEADER", relFiles, absFiles))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the files install generator.
|
||||||
|
if (!publicHeaderArgs.GetDestination().empty())
|
||||||
|
{
|
||||||
|
publicHeaderGenerator = CreateInstallFilesGenerator(absFiles,
|
||||||
|
publicHeaderArgs, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "TARGETS given no PUBLIC_HEADER DESTINATION for header files\""
|
||||||
|
<< target.GetName() << "\".";
|
||||||
|
this->SetError(e.str().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files = target.GetProperty("PRIVATE_HEADER");
|
||||||
|
if ((files) && (*files))
|
||||||
|
{
|
||||||
|
std::vector<std::string> relFiles;
|
||||||
|
cmSystemTools::ExpandListArgument(files, relFiles);
|
||||||
|
std::vector<std::string> absFiles;
|
||||||
|
if (!this->MakeFilesFullPath("PRIVATE_HEADER", relFiles, absFiles))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the files install generator.
|
||||||
|
if (!privateHeaderArgs.GetDestination().empty())
|
||||||
|
{
|
||||||
|
privateHeaderGenerator = CreateInstallFilesGenerator(absFiles,
|
||||||
|
privateHeaderArgs, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "TARGETS given no PRIVATE_HEADER DESTINATION for header files\""
|
||||||
|
<< target.GetName() << "\".";
|
||||||
|
this->SetError(e.str().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files = target.GetProperty("RESOURCE_FILES");
|
||||||
|
if ((files) && (*files))
|
||||||
|
{
|
||||||
|
std::vector<std::string> relFiles;
|
||||||
|
cmSystemTools::ExpandListArgument(files, relFiles);
|
||||||
|
std::vector<std::string> absFiles;
|
||||||
|
if (!this->MakeFilesFullPath("RESOURCE_FILES", relFiles, absFiles))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the files install generator.
|
||||||
|
if (!privateHeaderArgs.GetDestination().empty())
|
||||||
|
{
|
||||||
|
resourcesGenerator = CreateInstallFilesGenerator(absFiles,
|
||||||
|
resourcesArgs, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "TARGETS given no RESOURCES DESTINATION for resource files\""
|
||||||
|
<< target.GetName() << "\".";
|
||||||
|
this->SetError(e.str().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->Makefile->AddInstallGenerator(archiveGenerator);
|
this->Makefile->AddInstallGenerator(archiveGenerator);
|
||||||
this->Makefile->AddInstallGenerator(libraryGenerator);
|
this->Makefile->AddInstallGenerator(libraryGenerator);
|
||||||
this->Makefile->AddInstallGenerator(runtimeGenerator);
|
this->Makefile->AddInstallGenerator(runtimeGenerator);
|
||||||
this->Makefile->AddInstallGenerator(frameworkGenerator);
|
this->Makefile->AddInstallGenerator(frameworkGenerator);
|
||||||
this->Makefile->AddInstallGenerator(bundleGenerator);
|
this->Makefile->AddInstallGenerator(bundleGenerator);
|
||||||
this->Makefile->AddInstallGenerator(resourcesGenerator);
|
|
||||||
this->Makefile->AddInstallGenerator(publicHeaderGenerator);
|
this->Makefile->AddInstallGenerator(publicHeaderGenerator);
|
||||||
this->Makefile->AddInstallGenerator(privateHeaderGenerator);
|
this->Makefile->AddInstallGenerator(privateHeaderGenerator);
|
||||||
|
this->Makefile->AddInstallGenerator(resourcesGenerator);
|
||||||
|
|
||||||
if (!exports.GetString().empty())
|
if (!exports.GetString().empty())
|
||||||
{
|
{
|
||||||
this->Makefile->GetLocalGenerator()->GetGlobalGenerator()
|
this->Makefile->GetLocalGenerator()->GetGlobalGenerator()
|
||||||
->AddTargetToExports(exports.GetCString(),
|
->AddTargetToExports(exports.GetCString(), &target,
|
||||||
&target,
|
archiveGenerator, runtimeGenerator,
|
||||||
archiveGenerator,
|
libraryGenerator, frameworkGenerator,
|
||||||
runtimeGenerator,
|
bundleGenerator, publicHeaderGenerator);
|
||||||
libraryGenerator,
|
|
||||||
frameworkGenerator,
|
|
||||||
bundleGenerator);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,29 +614,9 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> absFiles;
|
std::vector<std::string> absFiles;
|
||||||
for(std::vector<std::string>::const_iterator
|
if (!this->MakeFilesFullPath(args[0].c_str(), files.GetVector(), absFiles))
|
||||||
fileIt = files.GetVector().begin(); fileIt != files.GetVector().end();
|
|
||||||
++fileIt)
|
|
||||||
{
|
{
|
||||||
// Convert this file to a full path.
|
return false;
|
||||||
std::string file = *fileIt;
|
|
||||||
if(!cmSystemTools::FileIsFullPath(file.c_str()))
|
|
||||||
{
|
|
||||||
file = this->Makefile->GetCurrentDirectory();
|
|
||||||
file += "/";
|
|
||||||
file += *fileIt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the file is not a directory.
|
|
||||||
if(cmSystemTools::FileIsDirectory(file.c_str()))
|
|
||||||
{
|
|
||||||
cmOStringStream e;
|
|
||||||
e << args[0] << " given directory \"" << (*fileIt) << "\" to install.";
|
|
||||||
this->SetError(e.str().c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Store the file for installation.
|
|
||||||
absFiles.push_back(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ica.Finalize())
|
if (!ica.Finalize())
|
||||||
|
@ -1011,3 +1065,33 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cmInstallCommand::MakeFilesFullPath(const char* modeName,
|
||||||
|
const std::vector<std::string>& relFiles,
|
||||||
|
std::vector<std::string>& absFiles)
|
||||||
|
{
|
||||||
|
for(std::vector<std::string>::const_iterator fileIt = relFiles.begin();
|
||||||
|
fileIt != relFiles.end();
|
||||||
|
++fileIt)
|
||||||
|
{
|
||||||
|
std::string file = (*fileIt);
|
||||||
|
if(!cmSystemTools::FileIsFullPath(file.c_str()))
|
||||||
|
{
|
||||||
|
file = this->Makefile->GetCurrentDirectory();
|
||||||
|
file += "/";
|
||||||
|
file += *fileIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the file is not a directory.
|
||||||
|
if(cmSystemTools::FileIsDirectory(file.c_str()))
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << modeName << " given directory \"" << (*fileIt) << "\" to install.";
|
||||||
|
this->SetError(e.str().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Store the file for installation.
|
||||||
|
absFiles.push_back(file);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -252,8 +252,9 @@ private:
|
||||||
bool HandleFilesMode(std::vector<std::string> const& args);
|
bool HandleFilesMode(std::vector<std::string> const& args);
|
||||||
bool HandleDirectoryMode(std::vector<std::string> const& args);
|
bool HandleDirectoryMode(std::vector<std::string> const& args);
|
||||||
bool HandleExportMode(std::vector<std::string> const& args);
|
bool HandleExportMode(std::vector<std::string> const& args);
|
||||||
void ComputeDestination(const char* destination, std::string& dest) const;
|
bool MakeFilesFullPath(const char* modeName,
|
||||||
bool CheckPermissions(std::string const& arg, std::string& permissions)const;
|
const std::vector<std::string>& relFiles,
|
||||||
|
std::vector<std::string>& absFiles);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "cmTarget.h"
|
#include "cmTarget.h"
|
||||||
|
|
||||||
#include "cmInstallExportGenerator.h"
|
#include "cmInstallExportGenerator.h"
|
||||||
|
#include "cmInstallFilesGenerator.h"
|
||||||
|
|
||||||
cmInstallExportGenerator::cmInstallExportGenerator(
|
cmInstallExportGenerator::cmInstallExportGenerator(
|
||||||
const char* destination,
|
const char* destination,
|
||||||
|
@ -60,7 +61,7 @@ bool cmInstallExportGenerator::AddInstallLocations(cmTargetWithProperties* twp,
|
||||||
std::string propertyName = prefix;
|
std::string propertyName = prefix;
|
||||||
propertyName += "LOCATION";
|
propertyName += "LOCATION";
|
||||||
// check that this property doesn't exist yet and add it then
|
// check that this property doesn't exist yet and add it then
|
||||||
if (twp->Properties.find(propertyName.c_str())== twp->Properties.end())
|
if (twp->Properties.find(propertyName.c_str()) == twp->Properties.end())
|
||||||
{
|
{
|
||||||
std::string destinationFilename = generator->GetDestination();
|
std::string destinationFilename = generator->GetDestination();
|
||||||
destinationFilename += "/";
|
destinationFilename += "/";
|
||||||
|
@ -83,7 +84,7 @@ bool cmInstallExportGenerator::AddInstallLocations(cmTargetWithProperties* twp,
|
||||||
propertyName += prefix;
|
propertyName += prefix;
|
||||||
propertyName += "LOCATION";
|
propertyName += "LOCATION";
|
||||||
// check that this property doesn't exist yet and add it then
|
// check that this property doesn't exist yet and add it then
|
||||||
if (twp->Properties.find(propertyName.c_str())== twp->Properties.end())
|
if (twp->Properties.find(propertyName.c_str()) == twp->Properties.end())
|
||||||
{
|
{
|
||||||
std::string destinationFilename = generator->GetDestination();
|
std::string destinationFilename = generator->GetDestination();
|
||||||
destinationFilename += "/";
|
destinationFilename += "/";
|
||||||
|
@ -100,6 +101,31 @@ bool cmInstallExportGenerator::AddInstallLocations(cmTargetWithProperties* twp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool cmInstallExportGenerator::AddInstallLocations(cmTargetWithProperties* twp,
|
||||||
|
cmInstallFilesGenerator* generator,
|
||||||
|
const char* propertyName)
|
||||||
|
{
|
||||||
|
if (generator == 0) // nothing to do
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((propertyName == 0) || (*propertyName == '\0'))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that this property doesn't exist yet and add it then
|
||||||
|
if (twp->Properties.find(propertyName) == twp->Properties.end())
|
||||||
|
{
|
||||||
|
twp->Properties[propertyName] = generator->GetDestination();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool cmInstallExportGenerator::SetExportSet(const char* name,
|
bool cmInstallExportGenerator::SetExportSet(const char* name,
|
||||||
const std::vector<cmTargetExport*>* set)
|
const std::vector<cmTargetExport*>* set)
|
||||||
{
|
{
|
||||||
|
@ -152,6 +178,11 @@ bool cmInstallExportGenerator::SetExportSet(const char* name,
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (this->AddInstallLocations(targetWithProps, (*it)->HeaderGenerator,
|
||||||
|
"PUBLIC_HEADER_LOCATION") == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -23,6 +23,7 @@ class cmTarget;
|
||||||
|
|
||||||
|
|
||||||
class cmInstallTargetGenerator;
|
class cmInstallTargetGenerator;
|
||||||
|
class cmInstallFilesGenerator;
|
||||||
|
|
||||||
/* cmInstallExportTarget is used in cmGlobalGenerator to collect the
|
/* cmInstallExportTarget is used in cmGlobalGenerator to collect the
|
||||||
install generators for the exported targets. These are then used by the
|
install generators for the exported targets. These are then used by the
|
||||||
|
@ -36,10 +37,12 @@ public:
|
||||||
cmInstallTargetGenerator* runtime,
|
cmInstallTargetGenerator* runtime,
|
||||||
cmInstallTargetGenerator* library,
|
cmInstallTargetGenerator* library,
|
||||||
cmInstallTargetGenerator* framework,
|
cmInstallTargetGenerator* framework,
|
||||||
cmInstallTargetGenerator* bundle
|
cmInstallTargetGenerator* bundle,
|
||||||
|
cmInstallFilesGenerator* headers
|
||||||
) : Target(tgt), ArchiveGenerator(archive),
|
) : Target(tgt), ArchiveGenerator(archive),
|
||||||
RuntimeGenerator(runtime), LibraryGenerator(library),
|
RuntimeGenerator(runtime), LibraryGenerator(library),
|
||||||
FrameworkGenerator(framework), BundleGenerator(bundle) {}
|
FrameworkGenerator(framework), BundleGenerator(bundle),
|
||||||
|
HeaderGenerator(headers) {}
|
||||||
|
|
||||||
cmTarget* Target;
|
cmTarget* Target;
|
||||||
cmInstallTargetGenerator* ArchiveGenerator;
|
cmInstallTargetGenerator* ArchiveGenerator;
|
||||||
|
@ -47,6 +50,7 @@ public:
|
||||||
cmInstallTargetGenerator* LibraryGenerator;
|
cmInstallTargetGenerator* LibraryGenerator;
|
||||||
cmInstallTargetGenerator* FrameworkGenerator;
|
cmInstallTargetGenerator* FrameworkGenerator;
|
||||||
cmInstallTargetGenerator* BundleGenerator;
|
cmInstallTargetGenerator* BundleGenerator;
|
||||||
|
cmInstallFilesGenerator* HeaderGenerator;
|
||||||
private:
|
private:
|
||||||
cmTargetExport();
|
cmTargetExport();
|
||||||
};
|
};
|
||||||
|
@ -85,6 +89,9 @@ protected:
|
||||||
static bool AddInstallLocations(cmTargetWithProperties *twp,
|
static bool AddInstallLocations(cmTargetWithProperties *twp,
|
||||||
cmInstallTargetGenerator* generator,
|
cmInstallTargetGenerator* generator,
|
||||||
const char* prefix);
|
const char* prefix);
|
||||||
|
static bool AddInstallLocations(cmTargetWithProperties* twp,
|
||||||
|
cmInstallFilesGenerator* generator,
|
||||||
|
const char* propertyName);
|
||||||
|
|
||||||
std::string Name;
|
std::string Name;
|
||||||
std::string FilePermissions;
|
std::string FilePermissions;
|
||||||
|
|
Loading…
Reference in New Issue