Commit 375dc492 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

#859 Remove obsolete C++ header guards modifier (a Python Script is to be used now).

parent d5ad2a41
/// \file
///
///
/// Created by Sebastien Gilles <sebastien.gilles@inria.fr> on the Fri, 26 Apr 2013 12:50:34 +0200
/// Copyright (c) Inria. All rights reserved.
///
#include "ThirdParty/IncludeWithoutWarning/Yuni/IO/File.hpp"
#include "ThirdParty/IncludeWithoutWarning/Yuni/IO/Directory/Iterator.hpp"
#include "ThirdParty/IncludeWithoutWarning/Yuni/Core/Logs.hpp"
#include "Utilities/Environment/EnvironmentVariable.hpp"
#include "Utilities/Filesystem/Folder.hpp"
using namespace Yuni;
namespace // anonymous
{
/*!
* \brief "Translate" a name of a directory (or of the project for that matter)
* to include guards name
*
* For instance, PictStock -> PICT_STOCK
*/
void Translate(YString& out, const AnyString& in);
using LoggingFacility = Logs::Logger<>;
enum { Ncharacters_read_on_line = 400 };
/*!
* \brief A derived class from HeaderGuardsIteratorIO::Directory::IIterator in charge
* of scanning the source directories and scan all header files
*
*/
class YUNI_DECL HeaderGuardsIterator : public IO::Directory::IIterator<false>,
private NonCopyable<HeaderGuardsIterator>
{
public:
//! Constructor & destructor
//@{
//! Constructor
HeaderGuardsIterator(LoggingFacility& logs, const AnyString& projectName,
const AnyString& rootFolder,
const std::vector<YString>& exclusion_list);
//! Destructor
virtual ~HeaderGuardsIterator();
//@}
//! Flow
using Flow = IO::Flow;
public:
//! Logging facility
LoggingFacility& logs_;
private:
//! Check the header guards for given header file and correct them if necessary
void checkHeaderGuards(const AnyString& filename);
//! Overload IIterator methods
//@{
virtual bool onStart(const YString& rootFolder) override;
virtual Flow onBeginFolder(const YString& completePath,
const YString&, const YString& name) override;
virtual void onEndFolder(const YString&, const YString&, const YString&) override;
virtual Flow onFile(const YString&, const YString& folder,
const YString& name, uint64 size) override;
virtual void onTerminate() override;
//@}
private:
//! Name of the project
YString pProjectName;
//! Index of first character to consider when translating into header guard
AnyString::size_type pLengthRootFolder;
/*!
* \brief The folders in this list will be ignored during file iteration; header guards inside will therefore
* be left unchanged.
*/
std::vector<YString> pExclusionList;
}; // Class HeaderGuardsIterator
void Translate(YString& out, const AnyString& in)
{
auto size = in.size();
// Consider apart the first character for which underscores are not added.
assert(size > 0);
assert(String::IsAlpha(in[0]));
out << static_cast<char>(String::ToUpper(in[0]));
// Then iterate on all other characters.
for (unsigned int i = 1u; i < size; ++i)
{
const char current = in[i];
if (String::IsAlpha(current))
{
const char upperCase = static_cast<char>(String::ToUpper(current));
if (upperCase == current)
out << '_' << current;
else
out << upperCase;
}
else if (String::IsDigit(current))
{
out << current;
}
else if (current == '.')
{
out << '_';
}
else
{
// Virtually anything else should be converted to '_x'
out << "_x";
}
}
if (out.last() != '_')
out << '_';
}
HeaderGuardsIterator::HeaderGuardsIterator(LoggingFacility& logs, const AnyString& projectName,
const AnyString& rootFolder, const std::vector<YString>& ignore_list)
: logs_(logs),
pProjectName(projectName),
pLengthRootFolder(rootFolder.size() + 1u),
pExclusionList(ignore_list)
{
add(rootFolder);
}
HeaderGuardsIterator::~HeaderGuardsIterator()
{
// For code robustness and to avoid corrupt vtable
stop();
}
bool HeaderGuardsIterator::onStart(const String& /*rootFolder*/)
{
return true;
}
HeaderGuardsIterator::Flow HeaderGuardsIterator::onBeginFolder(const String& full_path,
const String& , const String& /*name*/)
{
if (std::find(pExclusionList.cbegin(), pExclusionList.cend(), full_path) == pExclusionList.cend())
return IO::flowContinue;
return IO::flowSkip;
}
void HeaderGuardsIterator::onEndFolder(const String&, const String&, const String&)
{ }
HeaderGuardsIterator::Flow HeaderGuardsIterator::onFile(const String& filename,
const String& /*folder*/, const String& name, uint64 /*size*/)
{
CString<6, false> ext;
if (!IO::ExtractExtension(ext, name, false, false))
return IO::flowContinue;
// Criterion is extension that begins with h
if (!ext.startsWith('h'))
return IO::flowContinue;
checkHeaderGuards(filename);
return IO::flowContinue;
}
void HeaderGuardsIterator::onTerminate()
{ }
/*!
** \brief RAII for temporary file written with correct include guards
**
** Header file content is copied into NewFile, with header guards eventually corrected.
**
** If successful, calling function will yield temporary filename and copy it
** to replace previous version of header file.
**
*/
class NewFile
{
public:
//! Constructor and destructor
//@{
explicit NewFile(const AnyString& filename);
~NewFile();
//@}
//! Operator <<
template<class U>
inline NewFile& operator << (const U& u);
//! Name of the temporary filename
inline const YString& name() const { return pFileName; }
//! Close the stream
void close();
private:
//! Name of the temporary file
YString pFileName;
//! Stream
IO::File::Stream pStream;
};
NewFile::NewFile(const AnyString& filename)
: pFileName(filename)
{
pFileName << ".tmp";
pStream.openRW(pFileName);
}
NewFile::~NewFile()
{
if (pStream.opened())
pStream.close();
IO::File::Delete(pFileName);
}
template<class U>
NewFile& NewFile::operator << (const U& u)
{
pStream.operator << (u);
return *this;
}
void NewFile::close()
{
pStream.close();
}
void HeaderGuardsIterator::checkHeaderGuards(const AnyString& filename)
{
// Prepare message in case of failure
YString failureMsg("Couldn't modify automatically file ");
failureMsg << filename;
// Determine correct header guards
assert("If not, something fishy in the code" && filename.size() > pLengthRootFolder);
YString truncatedFilename(filename, pLengthRootFolder);
YString correctHeaderGuard;
{
// Determine the header guard to apply
Translate(correctHeaderGuard, pProjectName);
correctHeaderGuard += "x_";
Translate(correctHeaderGuard, truncatedFilename);
}
// Open the file to study
IO::File::Stream fileContent(filename, IO::OpenMode::read);
// Open a copy in which the modified content will be written
NewFile newFile(filename);
bool isFileUnchanged(true);
// Read line by line, and modify the three lines with header guards informations
{
YString line;
// The first relevant line should be line with the first header guard
// So line sbefore should be either empty or comments.
bool isComment(false);
while (fileContent.readline<Ncharacters_read_on_line>(line))
{
if (line.empty() || line.startsWith("//"))
{
newFile << line << '\n';
continue;
}
if (line.startsWith("/*"))
{
newFile << line << '\n';
isComment = true;
continue;
}
// If not in comment it means the line should be the header guard; which
// is handled just after the while loop
if (!isComment)
break;
if (line.endsWith("*/"))
{
isComment = false;
newFile << line << '\n';
}
}
if (!line.contains("ifndef"))
{
logs_.error(failureMsg + ": line beginning by ifndef not found at the beginning of the file.");
return;
}
{
YString expected("#ifndef ");
expected << correctHeaderGuard;
isFileUnchanged &= (line == expected);
newFile << expected << '\n';
}
// Second relevant line is line #define...
while (fileContent.readline<Ncharacters_read_on_line>(line) && line.empty());
if (!line.contains("define"))
{
std::cout << "Line = " << line << std::endl;
logs_.error(failureMsg + ": line beginning by define (after ifndef) not found at the beginning of the file.");
return;
}
{
YString expected("# define ");
expected << correctHeaderGuard;
isFileUnchanged &= (line == expected);
newFile << expected << '\n';
}
// Copy the rest of the file except the last relevant line,
// which should be endif one
YString lastRelevantLine;
// Reproduce the empty lines...
while (lastRelevantLine.empty())
fileContent.readline<Ncharacters_read_on_line>(lastRelevantLine, false);
unsigned int Nempty_lines = 0u;
while (fileContent.readline<Ncharacters_read_on_line>(line, false))
{
// If line is not empty, lastRelevant line might be updated!
if (!line.empty())
{
newFile << lastRelevantLine;
for (unsigned int i = 0u; i < Nempty_lines; ++i)
newFile << '\n';
lastRelevantLine = line;
Nempty_lines = 0u;
}
else
{
// If line is empty, do not print yet lastRelevantLin,e as it might be the very last one.
// Keep track of the numbers of empty lines.
++Nempty_lines;
}
}
// Print here all the empty lines stored.
for (unsigned int i = 0u; i < Nempty_lines; ++i)
newFile << '\n';
// LastRelevantLine should contain the last relevant line, with an endif in it
if (lastRelevantLine.empty())
{
logs_.error(failureMsg + + ": last relevant line empty at the end of the file.");
return;
}
if (!lastRelevantLine.contains("endif"))
{
logs_.error(failureMsg + + ": last relevant line does not begin with endif at the end of the file.");
return;
}
{
YString expected("#endif // ");
expected << correctHeaderGuard;
lastRelevantLine.trimRight('\n');
isFileUnchanged &= (lastRelevantLine == expected);
newFile << expected << '\n';
}
}
if (!isFileUnchanged)
{
std::cout << "File " << filename << " modified." << std::endl;
newFile.close();
IO::File::Copy(newFile.name(), filename);
IO::File::Delete(newFile.name());
}
}
} // namespace anonymous
/// @} // addtogroup UtilitiesGroup
int main(int argc, char** argv)
{
(void) argc;
(void) argv;
LoggingFacility logs;
std::cout << "[WARNING] Current executable will be deprecated shortly; please rather use Python script "
"${HOME}/Codes/HappyHeart/Scripts/header_guards.py." << std::endl;
std::vector<YString> ignore_list = { HappyHeart::Utilities::EnvironmentNS::SubstituteValues("${HOME}/Codes/HappyHeart/Sources/ThirdParty/Source") };
{
HeaderGuardsIterator iterator(logs, "HappyHeart", HappyHeart::Utilities::EnvironmentNS::SubstituteValues("${HOME}/Codes/HappyHeart/Sources"), ignore_list);
if (!iterator.start())
{
logs.fatal() << "Problem encountered while scanning source directory";
exit(EXIT_FAILURE);
}
iterator.wait();
}
//! Now look whether M3DISIM-HappyHeart content is also present. If so, run the code on it as well.
std::string m3didim_folder(HappyHeart::Utilities::EnvironmentNS::SubstituteValues("${HOME}/Codes/HappyHeart/M3DISIM"));
if (HappyHeart::FilesystemNS::Folder::DoExist(m3didim_folder))
{
HeaderGuardsIterator iterator(logs, "m3disim-HappyHeart", m3didim_folder, ignore_list);
if (!iterator.start())
{
logs.fatal() << "Problem encountered while scanning M3DISIM directory";
exit(EXIT_FAILURE);
}
iterator.wait();
}
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment