imgtools/romtools/rofsbuild/rofsbuild.cpp
author timothy.murphy@nokia.com
Thu, 25 Mar 2010 13:43:28 +0000
branchfix
changeset 408 a819f9223567
parent 0 044383f39525
child 590 360bd6b35136
child 606 30b30f9da0b7
permissions -rw-r--r--
fix: stop using "magic" numbers in string operations for the copyannofile2log feature fix: When using the copylogfromannofile workaround, extract the build ID and build duration and add to the log as these are useful for analysis. The log should now be identical to the stdout file. fix: Remove extra blank lines from output in copylogfromannofile mode.

/*
* Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
* @internalComponent * @released
* Rofsbuild mainfile to generate both rofs and data drive image.
*
*/


#include <string.h>
#include <stdlib.h>
#include <f32file.h>
#include "e32image.h"
#include "h_utl.h"
#include "h_ver.h"
#include "r_obey.h"
#include "r_driveimage.h"
#include "r_driveutl.h"
#include "r_coreimage.h"
#include "parameterfileprocessor.h"
#include "r_smrimage.h"

static const TInt RofsbuildMajorVersion=2;
static const TInt RofsbuildMinorVersion=6;
static const TInt RofsbuildPatchVersion=5;
static TBool SizeSummary=EFalse;
static TPrintType SizeWhere=EAlways;

static TInt gHeaderType=1;			// EPOC header
static TInt MAXIMUM_THREADS = 128;
static TInt DEFAULT_THREADS = 8;
ECompression gCompress=ECompressionUnknown;
TUint  gCompressionMethod=0;
TBool gFastCompress = EFalse;
TInt gThreadNum = 0;
TInt gCPUNum = 0;
char* g_pCharCPUNum = NULL;
TInt gCodePagingOverride = -1;
TInt gDataPagingOverride = -1;
TInt gLogLevel = 0;	// Information is logged based on logging level.
					// The default is 0. So all the existing logs are generated as if gLogLevel = 0.
					// If any extra information required, the log level must be appropriately supplied.
					// Currrently, file details in ROM (like, file name in ROM & host, file size, whether 
					// the file is hidden etc) are logged when gLogLevel >= LOG_LEVEL_FILE_DETAILS.

TBool gUseCoreImage=EFalse; // command line option for using core image file
char* gImageFilename=NULL;	// instead of obey file
TBool gEnableStdPathWarning=EFalse;// for in-correct destination path warning(executables).
TBool gLowMem=EFalse;

extern TBool gDriveImage;		// to Support data drive image.
TText* gDriveFilename=NULL;		// input drive oby filename.

string filename;				// to store oby filename passed to Rofsbuild.
TBool reallyHelp=EFalse;	

TBool gSmrImage = EFalse;
TText* gSmrFileName = NULL;

void PrintVersion()
	{
		Print(EAlways,"\nROFSBUILD - Rofs/Datadrive image builder");
		Print(EAlways, " V%d.%d.%d\n", RofsbuildMajorVersion, RofsbuildMinorVersion, RofsbuildPatchVersion);
		Print(EAlways,Copyright);
	}

char HelpText[] = 
	"Syntax: ROFSBUILD [options] obeyfilename(Rofs)\n"
	"Option: -v verbose,  -?,  -s[log|screen|both] size summary\n"
	"        -d<bitmask> set trace mask (DEB build only)\n"
	"        -compress   compress executable files where possible\n"
	"        -fastcompress  compress files with faster bytepair and tradeoff of compress ratio\n"
	"        -j<digit> do the main job with <digit> threads\n"
	"        -compressionmethod none|inflate|bytepair to set the compression\n"
	"              none     uncompress the image.\n"
	"              inflate  compress the image.\n"
	"              bytepair compress the image.\n"
	"        -coreimage <core image file>\n"
	"        -datadrive=<drive obyfile1>,<drive obyfile2>,... for driveimage creation\n"
	"              user can also input rofs oby file if required to generate both.\n"
	"        -smr=<SMR obyfile1>,<SMR obyfile2>,... for SMR partition creation\n"
	"        -loglevel<level>  level of information to log (valid levels are 0,1,2).\n"//Tools like Visual ROM builder need the host/ROM filenames, size & if the file is hidden.
	"        -wstdpath   warn if destination path provided for a file is not the standard path\n"
	"        -argfile=<FileName>   specify argument-file name containing list of command-line arguments\n"
	"        -lowmem     use memory-mapped file for image build to reduce physical memory consumption\n";

char ReallyHelpText[] =
	"Log Level:\n"
	"        0  produce the default logs\n"
	"        1  produce file detail logs in addition to the default logs\n"
	"        2  logs e32 header attributes in addition to the level 1 details\n";

void processParamfile(string aFileName);

/**
Process the command line arguments and prints the helpful message if none are supplied.
@param argc    - No. of argument.
@param *argv[] - Arguments value.
*/ 
void processCommandLine(int argc, char *argv[], TBool paramFileFlag=EFalse)
{
	// If "-argfile" option is passed to rofsbuild, then process the parameters
	// specified in parameter-file first and then the options passed from the 
	// command-line.
	string ParamFileArg("-ARGFILE=");	
	if(paramFileFlag == EFalse)
	{	
		for (int count=1; count<argc; count++)
		{
			string paramFile;
			strupr(argv[count]);
			if(strncmp(argv[count],ParamFileArg.c_str(),ParamFileArg.length())==0)
			{
				paramFile.assign(&argv[count][ParamFileArg.length()]);									
				processParamfile(paramFile);
			}
		}
	}	

	int i=1;
	while (i<argc)
		{
		strupr(argv[i]);
		if ((argv[i][0] == '-') || (argv[i][0] == '/'))
			{ // switch
			if (argv[i][1] == 'V')
				H.iVerbose = ETrue;
			else if(strncmp (argv[i], "-SMR=", 5) == 0)
			{
				if(argv[i][5])
				{
					gSmrImage = ETrue;
					gSmrFileName = (TText*)strdup(&argv[i][5]);
				}
				else
				{
					Print (EError, "SMR obey file is missing\n");
				}
			}
			else if (argv[i][1] == 'S')
				{
				SizeSummary=ETrue;
				if (argv[i][2] == 'L')
					SizeWhere=ELog;
				if (argv[i][2] == 'S')
					SizeWhere=EScreen;
				}
			else if (strncmp(argv[i],ParamFileArg.c_str(),ParamFileArg.length())==0)
				{
					if (paramFileFlag)
					{
						String paramFile;
						paramFile.assign(&argv[i][ParamFileArg.length()]);		
						processParamfile(paramFile);
					}
					else
					{
						i++;
						continue;
					}
				}
			else if (strcmp(argv[i], "-COMPRESS")==0)
				{
				gCompress=ECompressionCompress;
				gCompressionMethod = KUidCompressionDeflate;
				}
			else if (strcmp(argv[i], "-FASTCOMPRESS")==0)
				{
				gFastCompress=ETrue;
				}
			else if (strncmp(argv[i], "-J",2)==0)
				{
					if(argv[i][2])
						gThreadNum = atoi(&argv[i][2]);
					else
						{
						printf("WARNING: The option should be like '-j4'.\n");
						gThreadNum = 0;
						}
					if(gThreadNum <= 0 || gThreadNum > MAXIMUM_THREADS)
						{
						if(gCPUNum > 0 && gCPUNum <= MAXIMUM_THREADS)
							{
							printf("WARNING: The number of concurrent jobs set by -j should be between 1 and 128. And the number of processors %d will be used as the number of concurrent jobs.\n", gCPUNum);
							gThreadNum = gCPUNum;
							}
						else if(g_pCharCPUNum)
							{
							printf("WARNING: The number of concurrent jobs set by -j should be between 1 and 128. And the NUMBER_OF_PROCESSORS is invalid, so the default value %d will be used.\n", DEFAULT_THREADS);
							gThreadNum = DEFAULT_THREADS;
							}
						else
							{
							printf("WARNING: The number of concurrent jobs set by -j should be between 1 and 128. And the NUMBER_OF_PROCESSORS is not available, so the default value %d will be used.\n", DEFAULT_THREADS);
							gThreadNum = DEFAULT_THREADS;
							}
						}
				}
			else if (strcmp(argv[i], "-UNCOMPRESS")==0)
				{
				gCompress=ECompressionUncompress;
				}
			else if( strcmp(argv[i], "-COMPRESSIONMETHOD") == 0 )
				{
					// next argument should a be method
					if( (i+1) >= argc || argv[i+1][0] == '-') 
					{
					Print (EError, "Missing compression method! Set it to default (no compression)!");
					gCompressionMethod = 0;
					}
					else 
					{
					i++;
					strupr(argv[i]);
					if( strcmp(argv[i], "NONE") == 0)	
						{
						gCompress=ECompressionUncompress;
						gCompressionMethod = 0;	
						}
					else if( strcmp(argv[i], "INFLATE") == 0)
						{
						gCompress=ECompressionCompress;
						gCompressionMethod = KUidCompressionDeflate;	
						}	
					else if( strcmp(argv[i], "BYTEPAIR") == 0)
						{
						gCompress=ECompressionCompress;
						gCompressionMethod = KUidCompressionBytePair;	
						}
					else
						{
						Print (EError, "Unknown compression method! Set it to default (no compression)!");
						gCompress=ECompressionUnknown;
						gCompressionMethod = 0;		
						}
					}
					
				}
			else if (strcmp(argv[i], "-COREIMAGE") ==0)
				{
					gUseCoreImage = ETrue;

					// next argument should be image filename
					if ((i+1 >= argc) || argv[i+1][0] == '-')
						Print (EError, "Missing image file name");
					else
					{
						i++;
						gImageFilename = strdup(argv[i]);
					}
				}
			else if (strncmp(argv[i], "-DATADRIVE=",11) ==0)
				{  
				   	if(argv[i][11])	
						{
						gDriveImage = ETrue; 
						gDriveFilename = (TText*)strdup(&argv[i][11]);	
						}
					else
						{
						Print (EError, "Drive obey file is missing\n"); 
						}
				}
			else if (argv[i][1] == '?')
				{
				reallyHelp=ETrue;
				}
 			else if (strcmp(argv[i], "-WSTDPATH") ==0)		// Warn if destination path provided for a executables are incorrect as per platsec.		
 				{
 				gEnableStdPathWarning=ETrue;						
 				}
			
			else if( strcmp(argv[i], "-LOGLEVEL") == 0)
				{
				// next argument should a be loglevel
				if( (i+1) >= argc || argv[i+1][0] == '-')
					{
					Print (EError, "Missing loglevel!");
					gLogLevel = DEFAULT_LOG_LEVEL;
					}
				else
					{
					i++;
					if (strcmp(argv[i], "2") == 0)
						gLogLevel = (LOG_LEVEL_FILE_DETAILS | LOG_LEVEL_FILE_ATTRIBUTES);
					if (strcmp(argv[i], "1") == 0)
						gLogLevel = LOG_LEVEL_FILE_DETAILS;
					else if (strcmp(argv[i], "0") == 0)
						gLogLevel = DEFAULT_LOG_LEVEL;
					else
						Print(EError, "Only loglevel 0, 1 or 2 is allowed!");
					}
				}
			else if( strcmp(argv[i], "-LOGLEVEL2") == 0)
				gLogLevel = (LOG_LEVEL_FILE_DETAILS | LOG_LEVEL_FILE_ATTRIBUTES);
			else if( strcmp(argv[i], "-LOGLEVEL1") == 0)
				gLogLevel = LOG_LEVEL_FILE_DETAILS;
			else if( strcmp(argv[i], "-LOGLEVEL0") == 0)
				gLogLevel = DEFAULT_LOG_LEVEL;
			else if (strcmp(argv[i], "-LOWMEM") == 0)
				gLowMem = ETrue;
			else 
				cout << "Unrecognised option " << argv[i] << "\n";
			}
		else // Must be the obey filename
			filename=argv[i];
		i++;
		}
	
		if (paramFileFlag)
		return;

		if((gDriveImage == EFalse) && (gSmrImage ==  EFalse) && (filename.empty() || (gUseCoreImage && gImageFilename == NULL)))
		{
		PrintVersion();
		cout << HelpText;
		if (reallyHelp)
			{
			ObeyFileReader::KeywordHelp();
			cout << ReallyHelpText;
			}
		else if (filename.empty())
			{
			Print(EError, "Obey filename is missing\n");
			}
		}	
	}

/**
Function to process parameter-file. 

@param aFileName parameter-file name.
*/
void processParamfile(string aFileName)
{
	CParameterFileProcessor parameterFile(aFileName);
	
	// Invoke fuction "ParameterFileProcessor" to process parameter-file.
	if(parameterFile.ParameterFileProcessor())
	{
		TUint noOfParameters = parameterFile.GetNoOfArguments();
		char** parameters = parameterFile.GetParameters();
		TBool paramFileFlag=ETrue;

		// Invoke function "processCommandLine" to process parameters read from parameter-file.
		processCommandLine(noOfParameters,parameters,paramFileFlag);
	}	
}

/**
Main logic for data drive image creation. Called many types depending on no. of drive obey files.

@param aobeyFileName - Drive obey file.
@param alogfile      - log file name required for file system module.
 
@return - returns the status, after processing the drive obey file.
*/ 
TInt ProcessDataDriveMain(TText* aobeyFileName,TText* alogfile)
	{

	ObeyFileReader *reader=new ObeyFileReader(aobeyFileName);

	if(!reader)
		return KErrNoMemory;

	if(!reader->Open())
    {
        if (reader)
        {
            delete reader;
        }
		return KErrGeneral;
    }

	TInt retstatus =0;		
	CObeyFile* mainObeyFile=new CObeyFile(*reader);   
	CDriveImage* userImage = 0; 

	if(!mainObeyFile)
    {
        if (reader)
        {
            delete reader;
        }
		return KErrNoMemory;
    }

	// Process data drive image.
	// let's clear the TRomNode::sDefaultInitialAttr first, 'cause data drive is different from rom image
	TRomNode::sDefaultInitialAttr = 0; 
	retstatus = mainObeyFile->ProcessDataDrive();
	if (retstatus == KErrNone)
		{
		// Build a Data drive image using the description compiled into the CObeyFile object
		userImage = new CDriveImage(mainObeyFile);
		if(userImage)
			{	
			// Drive image creation.
			retstatus = userImage->CreateImage(alogfile);
			if(retstatus == KErrNone)
				{
				cout << "\nSuccessfully generated the Drive image : " << mainObeyFile->iDriveFileName << "\n";
				}
			else
				{
				cout << "\nFailed to generate the Image : " << mainObeyFile->iDriveFileName << "\n";
				}

			delete userImage;
			userImage = 0;
			}
		else
			{
			retstatus = KErrNoMemory;
			}
		}
	// restore
	TRomNode::sDefaultInitialAttr = (TUint8)KEntryAttReadOnly;
	cout << "\n-----------------------------------------------------------\n";
	
	delete mainObeyFile;
	delete reader;
	return retstatus;
	}

TInt ProcessSmrImageMain(TText* aObeyFileName, TText* /* alogfile */)
{
	ObeyFileReader *reader = new ObeyFileReader(aObeyFileName);
	if(!reader)
		return KErrNoMemory;
	if(!reader->Open())
    {
        if (reader)
        {
            delete reader;
        }
		return KErrGeneral;
    }
	TInt retstatus = 0;
	CObeyFile* mainObeyFile = new CObeyFile(*reader);
	CSmrImage* smrImage = 0;
	if(!mainObeyFile)
    {
        if (reader)
        {
            delete reader;
        }
		return KErrNoMemory;
    }

	if(mainObeyFile->Process())
	{
		smrImage = new CSmrImage(mainObeyFile);
		if(smrImage)
		{
			if((retstatus=smrImage->Initialise()) == KErrNone)
			{
				retstatus = smrImage->CreateImage();
			}
			if(retstatus == KErrNone)
			{
				cout << "\nSuccessfully generated the SMR image : " << smrImage->GetImageName().c_str() << "\n";
			}
			else
			{
				cout << "\nFailed to generate the Image : " << smrImage->GetImageName().c_str() << "\n";
			}
			delete smrImage;
		}
		else
		{
			retstatus = KErrNoMemory;
		}
	}
	delete mainObeyFile;
	delete reader;
	return retstatus;
}

/**
Rofsbuild Main function, which creates both Rofs and Data drive image.

@param argc    - No. of argument.
@param *argv[] - Arguments value.
  
@return - returns the status to caller.
*/ 
TInt main(int argc, char *argv[])
{
	TInt r =0;	

	g_pCharCPUNum = getenv("NUMBER_OF_PROCESSORS");
	if(g_pCharCPUNum != NULL)
		gCPUNum = atoi(g_pCharCPUNum);

 	processCommandLine(argc, argv);

 	TText *obeyFileName = NULL;	
 	if(!filename.empty())
 		obeyFileName=(TText*)filename.c_str();	

	if ((obeyFileName==NULL) && (gDriveFilename==NULL) && (gSmrFileName == NULL))                   
		return KErrGeneral;
	
	if(gThreadNum == 0)
	{
		if(gCPUNum > 0 && gCPUNum <= MAXIMUM_THREADS)
		{
			printf("The number of processors (%d) is used as the number of concurrent jobs.\n", gCPUNum);
			gThreadNum = gCPUNum;
		}
		else if(g_pCharCPUNum)
		{
			printf("WARNING: The NUMBER_OF_PROCESSORS is invalid, and the default value %d will be used.\n", DEFAULT_THREADS);
			gThreadNum = DEFAULT_THREADS;
		}
		else
		{
			printf("WARNING: The NUMBER_OF_PROCESSORS is not available, and the default value %d will be used.\n", DEFAULT_THREADS);
			gThreadNum = DEFAULT_THREADS;
		}
	}

	// Process drive obey files.
	if(gDriveImage)
	{  
		TText temp = 0;
		TText *driveobeyFileName = gDriveFilename;
		
		do
		{
			while(((temp = *gDriveFilename++) != ',') && (temp != 0));
			*(--gDriveFilename)++ = 0;
			
			if(*driveobeyFileName)
			{	
				TText* logfile = 0;
				if(Getlogfile(driveobeyFileName,logfile)== KErrNone)
				{
					H.SetLogFile(logfile);	
					PrintVersion();
					GetLocalTime();
					r = ProcessDataDriveMain(driveobeyFileName,logfile);   
					H.CloseLogFile();
					delete[] logfile;
					if(r == KErrNoMemory)
						return KErrNoMemory;
				}
				else
				{
					cout << "Error : Invalid obey file name : " << driveobeyFileName << "\n" ;   
				}
			}
			driveobeyFileName = gDriveFilename;
		}
		while(temp != 0);   
		
		gDriveImage=EFalse;
	} 
	if(gSmrImage)
	{
		TText temp = 0;
		TText *smrImageObeyFileName = gSmrFileName;
		do
		{
			while(((temp = *gSmrFileName++) != ',') && (temp != 0));
			*(--gSmrFileName)++ = 0;
			if(*smrImageObeyFileName)
			{	
				TText * logfile = 0;
				if(Getlogfile(smrImageObeyFileName,logfile) == KErrNone)
				{
					H.SetLogFile(logfile);
					PrintVersion();
					GetLocalTime();
					r = ProcessSmrImageMain(smrImageObeyFileName, logfile);
					H.CloseLogFile();
					delete[] logfile;
					if(r == KErrNoMemory)
						return KErrNoMemory;
				}
				else
				{
					cout << "Error: Invalid obey file name: " << smrImageObeyFileName << "\n";
				}
			}
			smrImageObeyFileName = gSmrFileName;
		}
		while(temp != 0);
		gSmrImage = EFalse;
	}
	// Process Rofs Obey files.
	if(obeyFileName)
	{
		
		H.SetLogFile((unsigned char *)"ROFSBUILD.LOG");	
		PrintVersion();
		
		ObeyFileReader *reader=new ObeyFileReader(obeyFileName);
		if (!reader->Open())
			return KErrGeneral;
		
		E32Rofs* RofsImage = 0;		// for image from obey file
		CCoreImage *core= 0;		// for image from core image file
		MRofsImage* imageInfo=0;
		CObeyFile *mainObeyFile=new CObeyFile(*reader);
		
		// need check if obey file has coreimage keyword
		TText *file = mainObeyFile->ProcessCoreImage();
		if (file)
		{
			// hase coreimage keyword but only use if command line option
			// for coreimage not already selected
			if (!gUseCoreImage)
			{
				gUseCoreImage = ETrue;
				gImageFilename = (char *)file;
			}
		}
		
		if (!gUseCoreImage)
		{
			
			r=mainObeyFile->ProcessRofs();
			if (r==KErrNone)
			{
				// Build a ROFS image using the description compiled into the
				// CObeyFile object
				
				RofsImage = new E32Rofs( mainObeyFile );
				if( !RofsImage )
				{
					return KErrNoMemory;
				}
				
				r = RofsImage->Create();
				if( KErrNone == r )
				{
					if(SizeSummary)
						RofsImage->DisplaySizes(SizeWhere);
					RofsImage->WriteImage( gHeaderType );
				}
				imageInfo = RofsImage;
				mainObeyFile->Release();
			}
			else if (r!=KErrNotFound)
				return r;
		}
		else
		{
			
			// need to use core image
			RCoreImageReader *reader = new RCoreImageReader(gImageFilename);
			if (!reader)
			{
				return KErrNoMemory;
			}
			core= new CCoreImage(reader);
			if (!core)
			{
				return KErrNoMemory;
			}
			r = core->ProcessImage();
			if (r != KErrNone)
				return r;
			imageInfo = core;
			mainObeyFile->SkipToExtension();
			
		}
		
		do 
		{
			CObeyFile* extensionObeyFile = 0;
			E32Rofs* extensionRofs = 0;
			
			extensionObeyFile = new CObeyFile(*reader);
			r = extensionObeyFile->ProcessExtensionRofs(imageInfo);
			if (r==KErrEof)
			{
				if(RofsImage)
					delete RofsImage;
				if(core)
					delete core;
				delete extensionObeyFile;
				return KErrNone;
			}
			if (r!=KErrNone)
				break;
			
			extensionRofs = new E32Rofs(extensionObeyFile);
			r=extensionRofs->CreateExtension(imageInfo);
			if (r!=KErrNone)
			{
				delete extensionRofs;
				delete extensionObeyFile;
				break;
			}
			if(SizeSummary)
				RofsImage->DisplaySizes(SizeWhere);
			r=extensionRofs->WriteImage(0);		
			delete extensionRofs;
			delete extensionObeyFile;
			extensionRofs = 0;
			extensionObeyFile = 0;
			
		}
		while (r==KErrNone);
		
		if(RofsImage) 
			delete RofsImage;									
		if(core)
			delete core;
		delete mainObeyFile;
		
	}
	return r;
}//end of main.