<?xml version="1.0" encoding="UTF-8"?>
<!--
============================================================================
Name        : compile.ant.xml
Part of     : Helium

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 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:

============================================================================
-->
<project name="compile" xmlns:hlm="http://www.nokia.com/helium">
    <description>
        Targets related to compile stage. It includes cmaker,
        ec, ebs, sbs, sbs-ec build system.
    </description>

    <!-- Property declaration -->
    <property name="genxml.output.file.suffix" value="genxml" />
    <property name="compile.sysdef.dtd.stub" location="${helium.dir}/tools/common/dtd/sysdef_dtd_1_4_0.xml" />
    <property name="compile.signal.input" value="compileSignalInput" />
    <property name="diamonds.compile.summary" location="${build.log.dir}/${build.id}_compile_diamonds.xml" />


    <!-- including common compilation macros -->
    <hlm:typedef file="${helium.dir}/tools/compile/compile.antlib.xml" uri="http://www.nokia.com/helium"/>
    <import file="cmaker.ant.xml" />
    <import file="ec/ec.ant.xml" />
    <import file="ebs/ebs.ant.xml" />
    <import file="sbs/sbs.ant.xml" />
    <import file="sis.ant.xml" />
    <import file="qt/qt.ant.xml" />

    <!-- A few basic steps (directory creation) that are needed before starting compilation. -->
    <target name="prebuild">
        <!-- Needed for the emulator to work. -->
        <mkdir dir="${build.drive}/epoc32/winscw/d"/>
        <!-- Build from source workaround as Symbian release is not completely unzipped. -->
        <mkdir dir="${build.drive}/epoc32/release/winscw/udeb/z/resource/fonts"/>
        <mkdir dir="${build.drive}/epoc32/release/winscw/urel/z/resource/fonts"/>
        <mkdir dir="${build.drive}/epoc32/include/osextensions"/>
    </target>

    <!-- Does all the necessary steps before starting the component build -->
    <target name="precompile" depends="record-compile-start-time,create-canonical-sysdef-file,precompile-ec"/>

    <!-- post operation for EC builds, submits the compile time and lists all the built components-->
    <target name="postcompile" depends="postcompile-ec,render-build-duplicates" unless="env.HLM_SUBCON">
        <antcall target="submit-compile-time"/>
    </target>
    
    <!-- Pre-processes all System Definition input files.

    Any Ant properties used in the files will be replaced by their values.
    Also the Symbian System Definition files needs to have \src inserted
    in front of the paths of the units.
    -->
    <target name="preprocess-sysdef-files">
        <mkdir dir="${build.output.dir}/build/input"/>
        <delete verbose="true">
            <fileset dir="${build.output.dir}/build/input/" includes="**"/>
        </delete>

        <fail message="No sysdef files found">
            <condition>
                <resourcecount refid="system.definition.files" when="equal" count="0" />
            </condition>
        </fail>

        <for param="file">
            <resources refid="system.definition.files"/>
            <sequential>
                <copy todir="${build.output.dir}/build/input"  verbose="true">
                    <fileset file="@{file}"/>
                    <filterchain>
                        <replaceregex pattern="bldFile=&quot;common" replace="bldFile=&quot;src\\\\common" flags="gi"/>
                        <replaceregex pattern="mrp=&quot;common" replace="mrp=&quot;src\\\\common" flags="gi"/>
                        <replaceregex pattern="bldFile=&quot;cedar" replace="bldFile=&quot;src\\\\cedar" flags="gi"/>
                        <replaceregex pattern="mrp=&quot;cedar" replace="mrp=&quot;src\\\\cedar" flags="gi"/>
                        <expandproperties/>
                    </filterchain>
                    <mapper>
                        <scriptmapper language="beanshell">
<![CDATA[
import java.io.File;
String counter = project.getProperty(".unique.counter");
int count;
if (counter == null){
    count = 0;
}else{
    count = Integer.valueOf(counter) + 1;
}
project.setProperty(".unique.counter", ""+count);
targetname = (String.format("%016d",count) + "_"+new File(source).getName());
self.addMappedName(targetname);
]]>
                        </scriptmapper>
                    </mapper>
                </copy>
            </sequential>
        </for>
    </target>


    <!-- Merges all preprocessed System Definition files into one combined file. -->
    <target name="create-canonical-sysdef-file" depends="preprocess-sysdef-files">
        <pathconvert pathsep=" " property="system.definition.files.list">
            <fileset dir="${build.output.dir}/build/input/" includes="*"/>
            <chainedmapper>
                <globmapper from="*" to="-x *" casesensitive="no"/>
            </chainedmapper>
        </pathconvert>
        <hlm:compileGenxmlMergeMacro input="-x ${compile.sysdef.dtd.stub} ${system.definition.files.list}"
                                    output="${canonical.sysdef.file}"/>
    </target>

    <!--
      Supports a cleaning of binaries before starting compilation, based on a SysDef configuration.
      <deprecated>Please consider using ido-prep-clean target.</deprecated>
    -->
    <target name="compile-clean" depends="create-canonical-sysdef-file"
      if="sysdef.clean.configuration">
        <antcall target="compile-configuration" inheritRefs="true">
            <param name="sysdef.configuration" value="${sysdef.clean.configuration}"/>
            <param name="compile.signal.input" value="compileCleanSignalInput"/>
            <param name="compile.discard.result" value="true"/>
        </antcall>
    </target>


    <!-- Creates a input file for EBS using Symbian genxml tool. -->
    <target name="genxml">
        <if>
            <or>
                <isset property="build.system.ebs"/>
                <isset property="build.system.ebs-ant"/>
            </or>
            <then>
                <hlm:compileGenxmlMacro input="-x ${canonical.sysdef.file}"
                                        configuration="${sysdef.configuration}"
                                        output="${genxml.output.file}"
                                        log="${build.log.dir}/${build.id}.${sysdef.configuration}_${genxml.output.file.suffix}.log" />
            </then>
        </if>
    </target>


    <!-- Postprocess genxml generated file by prefixing all commands with echos.

    Reads in the genxml generated command file (ready to be fed to EBS) and
    replaces all the command strings in the Execute statements with strings
    prefixed with echos.  For example, this target changes the following
    Execute statement:
    <pre>
      '<Execute ID="123" CommandLine="bldmake bldfiles -k"/>

    to:

      '<Execute ID="123" CommandLine="echo bldmake bldfiles -k"/>
    </pre>
    Properties:

      ebs.dry-run         When set, echo will be added to commands.
      genxml.output.file  The XML file with commands to be executed (by EBS).

    -->
    <target name="postprocess-genxml" if="ebs.dry-run">
      <!-- The 'echo' prefix will be added to the command even though it might
    already exist there. This is due to a problem with a regular expression;
    for some reason negative lookbehind assertion did not work due to syntax
    not being correct (python expression with less-than character within a
    python string within XML data; sounds messy and it was). -->
        <hlm:python>
import fileinput
import re

COMMANDLINE_RE = r'CommandLine="(.*?)"'
ECHO_REPLACEMENT = r'CommandLine="echo \1"'

for line in fileinput.input(["${genxml.output.file}"], inplace=True):
    print re.sub(COMMANDLINE_RE, ECHO_REPLACEMENT, line).rstrip()
        </hlm:python>
    </target>


    <!-- Takes a specific timestamp at the start of the compilation stage.

    This is used at the end of compilation to log the compile time to the GTI
    database.
    -->
    <target name="record-compile-start-time">
        <preset.exec executable="perl" outputproperty="build.main.start.time">
            <arg value="-e"/>
            <arg value="print time"/>
        </preset.exec>
    </target>

  <!-- Runs the Symbian scanlog and HTMLscanlog tools, as well as a Helium tool, for
  scanning the compilation log. -->
    <target name="compile-scanlog">
        <if>
            <!-- Only run is SBS is in use and sbs.hlm.scanlog is defined -->
            <or>
                <isset property="build.system.sbs"/>
                <isset property="build.system.sbs-ec"/>
            </or>
            <then>
                <if>
                    <not>
                        <isfalse value="${compile.discard.result}"/>
                    </not>
                    <then>
                        <delete file="${build.log.dir}/${build.id}.${sysdef.configuration}_compile.log.xml" failonerror="false"/>
                    </then>
                </if>
            </then>
            <else>
                <exec executable="perl" dir="${build.drive}/" output="${build.log.dir}/${build.id}.${sysdef.configuration}_scan1.log" failonerror="${failonerror}">
                    <arg value="${epocroot}epoc32/tools/scanlog.pl"/>
                    <arg file="${build.log.dir}/${build.id}.${sysdef.configuration}_compile.log"/>
                </exec>
                <exec executable="perl" dir="${build.drive}/" failonerror="${failonerror}">
                    <arg value="${epocroot}epoc32/tools/htmlscanlog.pl"/>
                    <arg value="-v"/>
                    <arg value="-v"/>
                    <arg value="-l"/>
                    <arg file="${build.log.dir}/${build.id}.${sysdef.configuration}_compile.log"/>
                    <arg value="-o"/>
                    <arg file="${build.log.dir}/${build.id}.${sysdef.configuration}_scan2.html"/>
                </exec>
                <hlm:assertFileExists file="${build.log.dir}/${build.id}.${sysdef.configuration}_scan2.html"/>
                <if>
                    <!--
                      Only generate the log.xml if we want the results to be included in the final results
                      or get them used by the signaling mechanism.
                    -->
                    <isfalse value="${compile.discard.result}"/>
                    <then>
                        <hlm:metadatarecord database="${metadata.dbfile}">
                            <hlm:abldmetadatainput>
                                <fileset casesensitive="false" file="${build.log.dir}/${build.id}.${sysdef.configuration}_compile.log" />
                                <metadatafilterset refid="filterset.compile" />
                            </hlm:abldmetadatainput>
                        </hlm:metadatarecord>
                        <hlm:compileLogSignalMacro compile.summary.file="${build.id}.${sysdef.configuration}_compile.log" 
                            error.limit="${build.errors.limit}" />
                        <!-- Todo: metadata: insert assertions for metadata parsing here -->
                    </then>
                </if>
            </else>
        </if>
    </target>

  <!-- Compiles one System Definition configuration.

  Certain properties must be defined to call this. It is intended to be called using
  an antcall from within a loop.
  -->
    <target name="compile-configuration" depends="run-qmake,genxml,postprocess-genxml,compile-ebs,compile-genxml-ec,
                        compile-ec-helium,compile-ebs-ant,compile-sbs,compile-scanlog"/>

  <!-- Executes each of the System Definition configurations in turn. -->
    <target name="do-compile-main">
        <propertyregex property="sysdef.configurations.list" input="${sysdef.configurations.list}"
                       regexp="\s*(\S+?)\s*,\s*(\S+?)\s*" replace="\1,\2" global="true" casesensitive="false"
                       override="true" defaultValue="${sysdef.configurations.list}"/>

        <for list="${sysdef.configurations.list}" delimiter="," param="sysdef.config">
            <sequential>
                <antcall target="compile-configuration" inheritRefs="true">
                    <param name="sysdef.configuration" value="@{sysdef.config}"/>
                    <param name="genxml.output.file" location="${temp.build.dir}/${build.id}.@{sysdef.config}_${genxml.output.file.suffix}.xml" />
                </antcall>
            </sequential>
        </for>
    </target>
    <var name="build.errors.total" value="" unset="true"/>

    <!-- Starts the actual compilation  phase of a build -->
    <target name="compile-main" depends="precompile,do-compile-main,postcompile"/>

    <!-- Macro to preprocess build information -->
    <scriptdef name="diamondsSummaryMacro" language="jython" uri="http://www.nokia.com/helium">
        <attribute name="output"/>
    <![CDATA[
from xml.sax.handler import ContentHandler
from xml.sax import make_parser
from xml.sax.saxutils import quoteattr

class DiamondsSummary(ContentHandler):
    def __init__(self):
        ContentHandler.__init__(self)
        self.scanLog = False
        self.components = {}
        self.component = None
        self.level = 0
        self.total = {'error':0, 'critical':0, 'warning':0}

    def startElement(self, name, attrs):
        if name == 'log' and attrs.get('filename', None) != None and attrs.get('filename', '').endswith('_compile.log'):
            self.scanLog = True
        elif self.scanLog and name == "task":
            self.level += 1

        if self.scanLog and name == "task" and self.level == 2 and attrs.get('name', None) != None:
            self.component = attrs.get('name', None)
            if self.component not in self.components:
                self.components[self.component] = {'error':0, 'critical':0, 'warning':0}

        if self.scanLog and name == "message" and attrs.get('priority', None) != None:
            if attrs.get('priority') in self.total:
                self.total[attrs.get('priority')] += 1

        if self.scanLog and name == "message" and self.level > 1 and self.component != None and attrs.get('priority', None) != None:
            if attrs.get('priority') in self.components[self.component]:
                self.components[self.component][attrs.get('priority')] += 1

    def endElement(self, name):
        if name == 'log':
            self.scanLog = False
            self.level = 0
            self.component = None
        elif name == 'task':
            self.level = self.level - 1


parser = make_parser()
diamondsSummary = DiamondsSummary()
parser.setContentHandler(diamondsSummary)
parser.parse(open(str(project.getProperty('build.summary.file'))))

output = '<?xml version="1.0"?>\n<compile>\n'
output += '<components>\n'
for name in diamondsSummary.components:
    xname = quoteattr(name)
    output += '<component name=%s error="%d" critical="%d" warning="%d" />\n' % (xname, diamondsSummary.components[name]['error'], diamondsSummary.components[name]['critical'], diamondsSummary.components[name]['warning'])
    #print name + " ==> %d" % diamondsSummary.components[name]['error']
output += '</components>\n'
output += '<total error="%d" critical="%d" warning="%d" />\n' % (diamondsSummary.total['error'], diamondsSummary.total['critical'], diamondsSummary.total['warning'])
output += '</compile>\n'
self.log(str("Writing %s" % str(attributes.get('output'))))
f = open(str(attributes.get('output')), 'w+')
f.write(output)
    ]]></scriptdef>

    <!-- Creates a .csv log of the sizes of all the binaries created in the build. -->
    <target name="binary-sizes-log" if="binary.sizes.output">
        <hlm:startSpecificLogMacro name="${build.log.dir}/${build.id}_binary_sizes.log"/>
        <property name="binary.sizes.output.file" location="${build.log.dir}/${build.id}_flash_image_size_data.csv"/>
        <pathconvert pathsep=";" property="build.logs.list">
            <fileset dir="${build.log.dir}" includes="*_build.log" excludes="*ant_build.log;*_zipup_build.log"/>
        </pathconvert>
        <pathconvert pathsep=";" property="binary.sizes.rom.logs.list">
            <fileset refid="binary.sizes.rom.logs"/>
        </pathconvert>
        <hlm:python>
import logging

import build.io
import sysdef.api
import sysdef.io

#logging.basicConfig(level=logging.DEBUG)
logging.basicConfig(level=logging.INFO)

logging.info('Reading the System Sefinition information')
sysDef = sysdef.api.SystemDefinition(r'${canonical.sysdef.file}')

# Read in the output binaries of each unit
logging.info('Reading the output binaries created by each unit.')
build_logs = r'${build.logs.list}'.split(';')
if len(build_logs) == 0:
    raise Exception('List of build logs is empty!')
logging.info("The list of log files:\n")
logging.info("\n".join(build_logs))
for logpath in build_logs:
    binaries_reader = build.io.AbldLogWhatReader(logpath)
    sysDef.merge_binaries(binaries_reader)

# Read in the binary sizes listed in the ROM output logs
logging.info('Reading the binary sizes of each binary from ROM logs.')
rom_logs = r'${binary.sizes.rom.logs.list}'.split(';')
if len(rom_logs) == 0:
    raise Exception('List of ROM logs is empty!')
logging.info("The list of log files:\n")
logging.info("\n".join(rom_logs))
for log in rom_logs:
    binary_sizes_reader = build.io.RombuildLogBinarySizeReader(log)
    sysDef.merge_binary_sizes(binary_sizes_reader)

# Write out a .csv file containing
size_writer = sysdef.io.FlashImageSizeWriter(r'${binary.sizes.output.file}')
size_writer.write(sysDef, r'${sysdef.configurations.list}'.split(','))
size_writer.close()
        </hlm:python>
        <hlm:assertFileExists file="${binary.sizes.output.file}"/>
        <hlm:stopSpecificLogMacro name="${build.log.dir}/${build.id}_binary_sizes.log"/>
    </target>


    <!-- Run codescanner to perform analysis on source code.
    Logs will be created under ${build.log.dir}/codescanner/
    Property ${codescanner.path} needs to be set to indicate what code to analyse.
    -->
    <target name="codescanner">
        <mkdir dir="${build.log.dir}"/>
        <mkdir dir="${build.log.dir}/codescanner"/>
        <hlm:codescanner dest="${build.log.dir}/codescanner" format="${codescanner.format}" configuration="${codescanner.config}" auto="${codescanner.log.unique}" >
            <path refid="codescanner.path"/>
        </hlm:codescanner>
        <move todir="${build.log.dir}/codescanner" includeemptydirs="false">
            <fileset dir="${build.log.dir}/codescanner" includes="*.*"/>
            <mapper type="glob" from="*" to="${build.id}_*"/>
        </move>
    </target>


    <!-- Updates the UAProf header.

    TODO: Is this still needed? -->
    <target name="update-uaprof-header">
        <property name="uaprof.cenrep.file" value="${build.drive}/epoc32/data/Z/private/10202be9/101F8731.txt"/>
        <copy file="${uaprof.cenrep.file}" tofile="${uaprof.cenrep.file}.original"/>
        <replace file="${uaprof.cenrep.file}" token="@version@" value="${build.version}" encoding="UTF-16LE" summary="true"/>
    </target>


  <!-- Runs a number of postbuild steps that must be run after compilation is complete,
  but before ROM images and zipping are started. -->
    <target name="postbuild" depends="update-uaprof-header,sisfiles" unless="env.HLM_SUBCON">
        <antcall target="security-checker"/>
        <antcall target="dump-swicertstore"/>
        <antcall target="check-stub-sis"/>
    </target>
</project>
