cdlcompiler/src/CdlCompiler.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 27 May 2010 13:53:15 +0300
changeset 25 1e3b56cca1f0
parent 1 b700e12870ca
permissions -rw-r--r--
Revision: 201019 Kit: 2010121

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#pragma warning (disable:4786)	// disable "identifier was truncated to '255' characters in the browser information" warning
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include <list>
#include <sstream>
#include <iomanip>
#include <memory>

#ifdef __MSVCRT__
#include <direct.h>
#endif

#include <CdlCompilerToolkit/CdlTkUtil.h>
#include <CdlCompilerToolkit/CdlTkProcess.h>
#include <CdlCompilerToolkit/CdlCompat.h>
using namespace std;
using namespace CdlCompilerToolkit;

#ifndef _DEBUG
#define EXCEPTION_HANDLING
#endif


typedef list<string> TArgsList;
auto_ptr<CCdlTkInterfaceList> ParseList(const TArgsList& aFiles);

//
// MainArgsErr
//

class MainArgsErr : public CdlCompilerToolkitErr
	{
public:
	MainArgsErr(const string& aExtraInfo = "");
	void Show(ostream& aStream) const;
private:
	string iExtraInfo;
	};

MainArgsErr::MainArgsErr(const string& aExtraInfo)
: iExtraInfo(aExtraInfo)
	{
	}

void MainArgsErr::Show(ostream& stream) const
	{
	if (iExtraInfo.size())
		{
		stream << endl;
		stream << iExtraInfo << endl;
		}
	stream << endl;
	stream << "CdlCompiler <mode> [<options>] <mode specific parameters>" << endl;
	stream << "  <mode> is:" << endl;
	stream << "    \"client\" mode creates the API for a customisable component." << endl;
	stream << "    \"instance\" mode creates customisation instance C++ files." << endl;
	stream << "    \"package\" mode creates package instance C++ files." << endl;
	stream << "    \"dll\" mode creates C++ and project files to collect together customisation instances in a DLL." << endl;
	stream << "    \"compare\" compares two CDL interfaces." << endl;
	stream << "  <options> are:" << endl;
	stream << "    -p<output path>  path for the output files." << endl;
	stream << "    -v               show CdlCompiler version." << endl;
	stream << "    -h               show help information for a <mode>." << endl;
	}


//
// CCompilerMode
//

class CCompilerMode 
	{
public:
	virtual ~CCompilerMode();
	virtual void ProcessOption(const std::string& aOpt);
	virtual int Process(const TArgsList& aArgs) = 0;
	virtual void ShowHelp(ostream& aStream) = 0;
	};

CCompilerMode::~CCompilerMode()
	{
	}

void CCompilerMode::ProcessOption(const std::string& aOpt)
	{
	throw MainArgsErr(string("unrecognised option for this mode ") + aOpt);
	}


//
// CClientMode
//

class CClientMode : public CCompilerMode
	{
public:
	int Process(const TArgsList& aArgs);
	void ShowHelp(ostream& aStream);
	};

int CClientMode::Process(const TArgsList& aArgs)
	{
	auto_ptr<CCdlTkInterfaceList> cdls(ParseList(aArgs));
	for (CCdlTkInterfaceList::iterator pCdl = cdls->begin(); pCdl != cdls->end(); ++pCdl)
		{
		CCdlTkWriteClientHeader processor(**pCdl);
		processor.Process();
		}
	return 0;
	}

void CClientMode::ShowHelp(ostream& aStream)
	{
	aStream << "CdlCompiler client <options> [<CDL files>]" << endl;
	aStream << "  client mode creates the API for a customisable component." << endl;
	aStream << "  For each CDL file parameter, it creates C++ header files containing the API." << endl;
	aStream << "  By default, these headers are placed in \\epoc32\\include." << endl;
	}

//
// CInstanceMode
//

class CInstanceMode : public CCompilerMode
	{
public:
	int Process(const TArgsList& aArgs);
	void ShowHelp(ostream& aStream);
	};

int CInstanceMode::Process(const TArgsList& aArgs)
	{
	TArgsList::const_iterator pArg = aArgs.begin();
	if (pArg == aArgs.end())
		throw MainArgsErr("Missing CDL file name");

	CCdlTkCdlFileParser parser(*pArg);
	auto_ptr<CCdlTkInterface> cdl = parser.LoadAndParse(true);

	++pArg;
	if (pArg == aArgs.end())
		throw MainArgsErr("Missing instance name");
	CCdlTkInstance inst(*cdl);
	inst.SetName(*pArg);
	inst.TemplateAllImplementations();

	++pArg;
	if (pArg != aArgs.end())
		{
		inst.SetId(CdlTkUtil::ParseInt(*pArg));
		++pArg;
		}

	if (pArg != aArgs.end())
		throw MainArgsErr("Too many parameters");

	CCdlTkWriteInstance processor(inst);
	processor.Process();
	
	return 0;
	}

void CInstanceMode::ShowHelp(ostream& aStream)
	{
	aStream << "CdlCompiler instance <options> <CDL file> <instance name> [<id>]" << endl;
	aStream << "  instance mode creates C++ files containing a template customisation instance." << endl;
	aStream << "  The customisation instance will implement the interface defined in <CDL file>." << endl;
	aStream << "  It and its source files will be called <instance name>." << endl;
	aStream << "  It will have an instance id of <id>, if specified. Otherwise," << endl;
	aStream << "  the instance id will come from the host DLL." << endl;
	}


//
// CPackageMode
//

class CPackageMode : public CCompilerMode
	{
public:
	int Process(const TArgsList& aArgs);
	void ShowHelp(ostream& aStream);
	};

int CPackageMode::Process(const TArgsList& aArgs)
	{
	TArgsList::const_iterator pArg = aArgs.begin();
	if (pArg == aArgs.end())
		throw MainArgsErr("Missing package CDL file name");

	CCdlTkCdlFileParser parser(*pArg);
	auto_ptr<CCdlTkInterface> cdl = parser.LoadAndParse(true);

	++pArg;
	if (pArg == aArgs.end())
		throw MainArgsErr("Missing package name");
	CCdlTkPackage pckg(*cdl);
	pckg.SetName(*pArg);
	pckg.TemplateAllImplementations();

	++pArg;
	if (pArg == aArgs.end())
		throw MainArgsErr("Missing package instance id");

	if (count_if(pArg->begin(), pArg->end(), CdlTkUtil::IsNumeric) == static_cast<signed int>( pArg->size()) )
		{
		pckg.SetId(CdlTkUtil::ParseInt(*pArg));
		++pArg;
		}

	for (; pArg != aArgs.end(); ++pArg)
		pckg.AddLocalContent(*pArg);

	CCdlTkWriteInstance processor(pckg);
	processor.Process();

	return 0;
	}

void CPackageMode::ShowHelp(ostream& aStream)
	{
	aStream << "CdlCompiler package <options> <CDL file> <package name> [<id>] [<instance names>]" << endl;
	aStream << "  package mode creates template package instance C++ files." << endl;
	aStream << "  The package instance will implement the interface defined in <CDL file>." << endl;
	aStream << "  It and its source files will be called <package name>." << endl;
	aStream << "  It will have an instance id of <id> if <id> is a number." << endl;
	aStream << "  The package contents will be references to the customisation instances <instance names>." << endl;
	aStream << "  Note: the package contents will assume that all instances are in the same DLL." << endl;
	}


//
// CDllMode
//

class CDllMode : public CCompilerMode
	{
public:
	CDllMode();
	void ProcessOption(const std::string& aOpt);
	int Process(const TArgsList& aArgs);
	void ShowHelp(ostream& aStream);
private:
	CCdlTkDll::CLibraries iLibs;
	string iExtraMmp;
	int iVersion;
	};

CDllMode::CDllMode()
: iVersion(1)
	{
	}

void CDllMode::ProcessOption(const std::string& aOpt)
	{
	if (aOpt[1] == 'l')
		{
		iLibs.push_back(aOpt.substr(2));
		}
	else if (aOpt[1] == 's')
		{
		CdlTkUtil::AppendString(iExtraMmp, string("SOURCE "));
		CdlTkUtil::AppendString(iExtraMmp, aOpt.substr(2));
		CdlTkUtil::AppendString(iExtraMmp, "\n");
		}
	else if (aOpt[1] == 'e')
		{
		CdlTkUtil::AppendString(iExtraMmp, aOpt.substr(2));
		CdlTkUtil::AppendString(iExtraMmp, "\n");
		}
	else if(aOpt[1] == 'n')
		{
		iVersion = CdlTkUtil::ParseInt(aOpt.substr(2));
		if(iVersion < 1) iVersion = 1;
		}
	else
		{
		CCompilerMode::ProcessOption(aOpt);
		}
	}

int CDllMode::Process(const TArgsList& aArgs)
	{
	TArgsList::const_iterator pArg = aArgs.begin();
	if (pArg == aArgs.end())
		throw MainArgsErr("Missing DLL name");

	CCdlTkDll dll;
	dll.SetName(*pArg);
	++pArg;

	dll.SetVersion(iVersion);

	if (pArg == aArgs.end())
		throw MainArgsErr("Missing DLL UID");

	dll.SetUid(CdlTkUtil::ParseInt(*pArg));
	++pArg;

	for (; pArg != aArgs.end(); ++pArg)
		{
		const string& inst = *pArg;
		if (inst[0]!='@')
			{
			dll.AddInstance(*pArg);
			}
		else
			{
			// read instance list from a file
			ifstream argFile;
			CdlTkUtil::OpenInput(argFile, inst.substr(1));
			string line;
			while (!argFile.eof())
				{
				getline(argFile, line);
				if (line.length())
					dll.AddInstance(line);
				}
			argFile.close();
			}
		}

	dll.Libraries().insert(dll.Libraries().end(), iLibs.begin(), iLibs.end());
	dll.SetExtraMmp(dll.ExtraMmp() + iExtraMmp);

	CCdlTkWriteDll processor(dll);
	processor.Process();

	return 0;
	}

void CDllMode::ShowHelp(ostream& aStream)
	{
	aStream << "CdlCompiler dll <options> <dll name> <uid> [<instance names>]" << endl;
	aStream << "  dll mode creates C++ and project files to collect together customisation instances in a DLL." << endl;
	aStream << "  The DLL and its source files will be called <dll name>." << endl;
	aStream << "  It will have a UID of <uid>." << endl;
	aStream << "  The DLL will contain customisation instances <instance names>." << endl;
	aStream << "  If an instance name starts with @, this will be treated as the name of a file" << endl;
	aStream << "  which contains a list of instance names, one per line." << endl;
	aStream << "  Options:" << endl;
	aStream << "    -l<libName> to add a library to the DLL." << endl;
	aStream << "    -s<sourceFileName> to add a source file to the DLL." << endl;
	aStream << "    -e<extraMmp> to add more statements to the DLL's MMP file." << endl;
	aStream << "    -n<version> to set the DLL version number in format x, defaults to 1 when not specified or invalid." << endl;
	aStream << "          Does not accept a minor version number." << endl;
	}


//
// CCompareModeChecker
//

class CCompareModeChecker : public MCdlTkApiCheckObserver
	{
public:
	CCompareModeChecker(const string& aLeft, const string& aRight);
	virtual ~CCompareModeChecker();
	virtual void StartCheck();
	virtual void CheckComplete();
	virtual void ApiInBoth(const CCdlTkApi& aApi);
	virtual void ApiNotInLeft(const CCdlTkApi& aApi);
	virtual void ApiNotInRight(const CCdlTkApi& aApi);
	
private:
	int iErrs;
	string iLeft;
	string iRight;
	};

CCompareModeChecker::CCompareModeChecker(const string& aLeft, const string& aRight)
: iLeft(aLeft), iRight(aRight)
	{
	}

CCompareModeChecker:: ~CCompareModeChecker()
    {

    }

void CCompareModeChecker::StartCheck()
	{
	iErrs = 0;
	}

void CCompareModeChecker::CheckComplete()
	{
	cout << "Check complete, " << iErrs << " differences found" << endl;
	}

void CCompareModeChecker::ApiInBoth(const CCdlTkApi& /*aApi*/)
	{
	}

void CCompareModeChecker::ApiNotInLeft(const CCdlTkApi& aApi)
	{
	cout << iRight << ": " << aApi.Name() << aApi.ParamsTypeAndNameList() << " not found in " << iLeft << endl;
	iErrs++;
	}

void CCompareModeChecker::ApiNotInRight(const CCdlTkApi& aApi)
	{
	cout << iLeft << ": " << aApi.Name() << aApi.ParamsTypeAndNameList() << " not found in " << iRight << endl;
	iErrs++;
	}


//
// CCompareMode
//

class CCompareMode : public CCompilerMode
	{
public:
	int Process(const TArgsList& aArgs);
	void ShowHelp(ostream& aStream);
	};

int CCompareMode::Process(const TArgsList& aArgs)
	{
	auto_ptr<CCdlTkInterfaceList> cdls(ParseList(aArgs));
	if (cdls->size() != 2)
		throw MainArgsErr("Exactly 2 CDL files required");
	CCompareModeChecker reporter(*aArgs.begin(), *++(aArgs.begin()));
	CCdlTkApiChecker checker(*(*cdls)[0], *(*cdls)[1], reporter);
	checker.Process();

	return 0;
	}

void CCompareMode::ShowHelp(ostream& aStream)
	{
	aStream << "CdlCompiler compare <options> <left CDL file> <right CDL file>" << endl;
	aStream << "  compare mode reports the difference between two CDL interfaces." << endl;
	}


//
// CCompareMode
//

class CBigApiTestMode : public CCompilerMode
	{
public:
	int Process(const TArgsList& aArgs);
	void ShowHelp(ostream& aStream);
	};

string MakeApiName(int i)
	{
	return string("Api")+CdlTkUtil::IntToString(i);
	}

int CBigApiTestMode::Process(const TArgsList& aArgs)
	{
	string name = "BigApi";
	int size = CdlTkUtil::ParseInt(*aArgs.begin());

	// make a CDL interface
	CCdlTkInterface iface;
	iface.SetFileName(name+".cdl");
	CCdlTkInterfaceHeader& header = iface.Header();
	header.SetName(name);
	header.SetUid(0x0fffffff);
	CCdlTkApiList& apiList = iface.ApiList();
	for (int ii=0; ii<size; ii++)
		{
		auto_ptr<CCdlTkFunctionApi> api(new CCdlTkFunctionApi(iface));
		api->SetName(MakeApiName(ii));
		api->SetReturnType("TInt");
		apiList.push_back(api.get());
		api.release();
		}

	// write the CDL file
	CCdlTkWriteCdlFile cdlWriter(iface);
	cdlWriter.Process();

	// make an instance
	CCdlTkInstance inst(iface);
	inst.SetName(name+"Inst");
	inst.TemplateAllImplementations();
	CCdlTkImplementations& impls = inst.Impl();
	for (CCdlTkImplementations::iterator pImpl = impls.begin(); pImpl != impls.end(); ++pImpl)
		{
		CCdlTkImplementation& imp = **pImpl;
		string defn = imp.Definition();
		defn = CdlTkUtil::Replace("//TODO: Implement this function.", "return 0;", defn);
		imp.SetDefinition(defn);
		}

	// write the instance
	CCdlTkWriteInstance instWriter(inst);
	instWriter.Process();

	return 0;
	}

void CBigApiTestMode::ShowHelp(ostream& /*aStream*/)
	{
	}


//
// static functions
//

bool ProcessOptions(TArgsList::iterator& aArgIter, const TArgsList::iterator& aEnd, CCompilerMode& aMode)
	{
	while (aArgIter != aEnd && (*aArgIter)[0] == '-')
		{
		string& arg = *aArgIter;
		if (arg.size() < 2)
			throw MainArgsErr("bad option \"-\"");
		switch (arg[1])
			{
			case 'p': 
				CdlTkUtil::SetOutputPath(arg.substr(2));
				break;
			case 'v': 
				cout << "CdlCompiler version " << KCdlCompilerMajorVersion << "." << KCdlCompilerMinorVersion << endl;
				return true;		// exit CdlCompiler
			case 'h': 
				aMode.ShowHelp(cerr);
				return true;		// exit CdlCompiler
			default: 
				aMode.ProcessOption(arg);
				break;
			}
		++aArgIter;
		}
	return false;	// continue execution of the CdlCompiler
	}

auto_ptr<CCdlTkInterfaceList> ParseList(const TArgsList& aFiles)
	{
	auto_ptr<CCdlTkInterfaceList> ifaceList(new CCdlTkInterfaceList);

	for (TArgsList::const_iterator pFile = aFiles.begin(); pFile != aFiles.end(); ++pFile)
		{
		CCdlTkCdlFileParser parser(*pFile);
		auto_ptr<CCdlTkInterface> cdl = parser.LoadAndParse(true);
		ifaceList->push_back(cdl.get());
		cdl.release();
		}

	return ifaceList;
	}

int DoMain(int argc, char* argv[])
	{
	CdlTkUtil::SetCommandLine(argc, argv);

	if (argc < 2)
		throw MainArgsErr("Missing mode");

	TArgsList args;
	copy(argv + 1, argv + argc, back_inserter(args));
	TArgsList::iterator pArg = args.begin();

	// process mode arg
	auto_ptr<CCompilerMode> mode;
	if (*pArg == "client")
		{
		mode = auto_ptr<CCompilerMode>(new CClientMode);
		CdlTkUtil::SetOutputPath(CdlTkUtil::CurrentDrive() +PATHSEP+"epoc32"+PATHSEP+"include"+PATHSEP);
		}
	else if (*pArg == "instance")
		{
		mode = auto_ptr<CCompilerMode>(new CInstanceMode);
		}
	else if (*pArg == "dll")
		{
		mode = auto_ptr<CCompilerMode>(new CDllMode);
		}
	else if (*pArg == "package")
		{
		mode = auto_ptr<CCompilerMode>(new CPackageMode);
		}
	else if (*pArg == "compare")
		{
		mode = auto_ptr<CCompilerMode>(new CCompareMode);
		}
	else if (*pArg == "BigApiTest")
		{
		mode = auto_ptr<CCompilerMode>(new CBigApiTestMode);
		}
	else
		throw MainArgsErr(string("Unrecognised mode ") + *pArg);
	++pArg;

	// process options args
	if (ProcessOptions(pArg, args.end(), *mode))
		return 0;

	// process mode args
	return mode->Process(TArgsList(pArg, args.end()));
	}

int main(int argc, char* argv[])
	{
#ifdef EXCEPTION_HANDLING
	try
		{
#endif
		return DoMain(argc, argv);
#ifdef EXCEPTION_HANDLING
		}
	catch (const CdlCompilerToolkitErr& aErr)
		{
		aErr.Show(cerr);
		}

	return 1;
#endif
	}

// End of File