Feeds:
Posts
Comments

Archive for March, 2013

Always make a plan first

Try to ask yourself the followings:
1. What tasks you want NAnt to help you?
2. What is any specific environment parameters you needed?
3. What is the possible variables that may change (even though you’ll never change it at last)?

VB.net Hello World

Suppose we want to write a VB.net Hello world to simply display a string “Hello, world!”.

1. We create the following folder structure under a folder called “~/source”:
HelloWorld
-src
-build
We can use the following commands to create these folders:

mkdir -p ~/source/HelloWorld/src
mkdir -p ~/source/HelloWorld/src

2. Save the following source code into ~/source/HelloWorld/src/HelloWorld.vb

Namespace org.examplet
	Module HelloWorld
		Sub Main
			System.Console.WriteLine("Hello, world!")
		End Sub
	End Module
End Namespace

The use of Namespace is to differentiate different version of “HelloWorld”. Cloudgen want to post this version to “examplet.org” in later time, thus, Cloudgen add “Namespace org.examplet” in the first line. It is our practice to write the namespace as the reverse order of domain name:

examplet.org -> org.examplet

Inside the namespace is the “Module HelloWorld”. We use module to represent the program’s name. If you have a program called HelloFriend, you should write “Module HelloFriend”.

Module is built from Subroutine (sub). We have one subroutine only, which is “Sub Main”. Cloudgen tried to use System.Console.WriteLine command to write “Hello, world” to the console.

Planning NAnt

We want to use NAnt to help us to do any of the following tasks:
1. Clean the build directory: If we keep on developing the project, we may need a clean directory to make sure no previous built code left in the folder.
2. Generate the compiled code
3. Execute the compiled code
4. Clean the build directory and Generate compiled code for delivery.

Thus, we have four different tasks. In NAnt jargon, we have four targets. Let’s name the four targets as:
a. clean
b. build
c. run
d. clean-build

In NAnt, we need to have XML tags representing these targets:

	<target name="clean"/>
	<target name="build"/>
	<target name="run"/>
	<target name="clean-build"/>

NAnt check for any file which has extension “build”. We usually save a “default.build” file to tell what NAnt need to do. This file is made up of tags which form a XML-format file.

The dependency between targets

Sometime we need to compile or build (target:build) the generated code before executing (target:run) the code. This create dependency: target run depends build.

In the clean-build target, it just two process combined from target:clean and target:build. This can be done by creating dependency on these two targets. Thus in NAnt build file, we need to change the target tags into:

	<target name="clean"/>
	<target name="build"/>
	<target name="run" depends="build"/>
	<target name="clean-build" depends="clean,build"/>

Dot Net Environment

We don’t concern too much about dot-net environment. The only thing we need to know is should we target the code for specific mono version. In order to experiment this, we assume we need to compile the code to mono-4.0 only.

In NAnt, we need to set a property called “nant.settings.currentframework” to specific mono version, for instance:

<property name="nant.settings.currentframework" value="mono-4.0"/>

Variables

Where is the source code? Where will the generated file stored? What is the executable file’s name? …

Those are possible variables to a project. We usually have the following standard variables:
1. project name
2. base directory
3. default task
4. build directory
5. executable file

The first three variables are standard attributes which should be mention in the outmost tag called, “project” tag, in our case:

<project name="HelloWorld" basedir="." default="run">

The rest of the variables can be stored as property using “property” tags:

	<property name="src.dir" value="src"/>
	<property name="build.dir" value="build"/>
	<property name="output.file" value="build/HelloWorld.exe"/>

You may notice that the executable file is stored in the build directory, we can rewrite it into:

	<property name="output.file" value="${build.dir}/HelloWorld.exe"/>

Moreover, the output filename should be the same as the project’s name. So, we can use the following expression:

	<property name="output.file" value="${build.dir}/${project::get-name()}.exe"/>

Clean directory by “delete task”

NAnt delete task can be used to delete files or directly. In our case, we need to use NAnt to remove a directory called “build”, or equivalent to ${build.dir}, we can use the following tags:

	<delete dir="${build.dir}"/>

We put this command inside the target clean, we have the following:

	<target name="clean">
		<delete dir="${build.dir}"/>
	</target>

Create directory by “mkdir task”

A directory can be created by mkdir tasks. In our case the ${build.dir} can be created by:

	<mkdir dir="${build.dir}"/>

We put this command inside the task for compiling file (target:build)

	<target name="build">
		<mkdir dir="${build.dir}"/>
	</target>

Compile VB.Net code by VBNC or VBC

In mono environment, we use “vbnc” command to compile vb.net source code, however, we use “vbc” to compile vb.net source code in microsoft dot net environment.

It’s common to place all the source code files (more than one) under the source directory (or under sub directory). We use “**” to represent any sub-directory, and use “*.vb” to represent any files with “vb” as extension.

In NAnt jargon, to compile files to an executable file ${output.file}, should look like:

	<vbc target="exe" output="${output.file}">
		<sources>
			<include name="${src.dir}/**/*.vb"/>
		</sources>
	</vbc>

If we put those code under the target build, the target build should look like:

	<target name="build">
		<mkdir dir="${build.dir}"/>
		<vbc target="exe" output="${output.file}">
			<sources>
				<include name="${src.dir}/**/*.vb"/>
			</sources>
		</vbc>
	</target>

Execute the output file

To execute the output file ${output.file} is not difficult. Under mono environment, all the output file can be executed by “mono” command. Thus, we can ask NAnt to use mono to execute the ${output.file} by the following tag:

	<exec program="mono">
		<arg value="${output.file}"/>
	</exec>

After inserting these code into target run, we’ll have the followings:

	<target name="run" depends="build">
		<exec program="mono">
			<arg value="${output.file}"/>
		</exec>
	</target>

The final code

We put everything together into an XML format file, the file should become:

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="HelloWorld" basedir="." default="run">
	<property name="nant.settings.currentframework" value="mono-4.0"/>
	<property name="src.dir" value="src"/>
	<property name="build.dir" value="build"/>
	<property name="output.file" value="${build.dir}/${project::get-name()}.exe"/>
	<target name="clean">
		<delete dir="${build.dir}"/>
	</target>
	<target name="build">
		<mkdir dir="${build.dir}"/>
		<vbc target="exe" output="${output.file}">
			<sources>
				<include name="${src.dir}/**/*.vb"/>
			</sources>
		</vbc>
	</target>
	<target name="run" depends="build">
		<exec program="mono">
			<arg value="${output.file}"/>
		</exec>
	</target>
	<target name="clean-build" depends="clean,build"/>
</project>

Read Full Post »

Cloudgen always like to measure the elapsed time for finishing a small task. So, Cloudgen has written a BASH shell script to take the current time:

#!/bin/bash
starttime=$(date +%s)
export starttime

Save this file as /usr/bin/taketime and add the execution right:

chmod +x /usr/bin/taketime

However, if you need to calculate the time difference, you need another shell script:

#!/bin/bash
endtime=$(date +%s)
elapse=$(($endtime - $starttime))
echo "$(($elapse / 60)):$(($elapse % 60))"

Save this file as /usr/bin/elapse and add the corresponding execution right:

chmod +x /usr/bin/elapse

Now, if you need to measure the time difference for the command (e.g. doSomething)

source taketime
doSomething
elapse

The result should be using minutes as unit, e.g.

0:25

means 25 seconds.

Read Full Post »

Some sharings

This is the second time Cloudgen tried to configure NAnt to run under Mono 3.06 Beta for Mac OSX. As I can recall, I had spent some time to research on how to fix the nant shell script.

This time, Cloudgen has already installed Mono 3.06 Beta in Mountain Tiger, install NAnt through MacPort Command.
If you want to download Mono 3.06 beta, you can go to: http://www.go-mono.com/mono-downloads/download.html

My testing configuration

1. OS: Mountain Lion (Mac OS X 10.8.2)
2. Mono: Version 3.0.6
3. NAnt: 0.91

Pre-requisite

Before you install NAnt, you need to install MacPort, please refers: http://www.macports.org/install.php

After installing MacPort, you can use the following command:

sudo port install nant

The system will prompt you for administrator’s password. After filling the correct administrator password, the installation will be automatic.

However, when you tried to write a defualt.build file and run it by the nant command:

nant

You’ve got the followings:

-bash: nant: command not found

This is because, the port command don’t help you to create a shell script to execute NAnt.

If you don’t have this problem, try to see where is your nant:

which nant

Most likely your nant script have been generated to: /opt/local/bin/nant
Your still have the probability to have the following problems:


Using spotlight or browsing folders, you may find the NAnt should be installed in /opt/local/share/ and the execution path for NAnt should be:

/opt/local/share/NAnt/bin/NAnt.exe

Errors and errors and keeping errors

If you run the command directly, you find an other error:

BUILD FAILED

Failed to initialize the 'Mono 3.5 Profile' (mono-3.5) target framework.

    Unable to locate 'mono' module using pkg-config. Download the Mono development packages from http://www.mono-project.com/downloads/.

For more information regarding the cause of the build failure, run the build again in debug mode.

Try 'nant -help' for more information

This is caused by NAnt.exe when it tried to use pkg-config to search for the path information for mono.

If you type the following command to simulate the process which NAnt tried:

pkg-config --modversion mono

You will see the following errors:

Package mono was not found in the pkg-config search path.
Perhaps you should add the directory containing `mono.pc'
to the PKG_CONFIG_PATH environment variable
No package 'mono' found

The solutions

In order to solve this, we can write a shell script for running NAnt.exe ourself under /usr/bin by any text editor or the following command:

sudo nano /usr/bin/nant

The source code for the shell script should be (for my MacBook Air):

#!/bin/sh
export PKG_CONFIG_PATH=/opt/local/lib/pkgconfig:/Library/Frameworks/Mono.framework/Versions/Current/lib/pkgconfig
exec mono /opt/local/share/NAnt/bin/NAnt.exe "$@"

After saving the file as /usr/bin/nant, you should add execute rights:

chmod a+x /usr/bin/nant

The problem for the running NAnt for Mono-3 beta under Mac OSX should be solved.

Read Full Post »

join an array from another join operation may cause memory address calculation error
====================================================================================
最近用JScript寫一個不斷將文件夾的內容翻譯成ASP文件的程式。

這個程式的副檔名為wsf,每次能將65536個文件夾的內容閱讀並儲成65536個ASP文件,每次休息60分鐘再重覆。可是,每一至二天就會有亂碼的文件出現。出現的文件每次都不相同,可是出現在文件的相同的位置,第二行的頭兩個數據。

ASP文件的內容由以下的片段寫成,出現亂碼的地方是fileList及fileArray的第一、二個。這個片段的特點是將join後的結果再join。 

var sCode=['<',
  'script language="JScript" runat="server"','>\n',
  'var filePartList={',fileList.join(','),'};\n',
  'var filePartArray=[',fileArray.join(','),'];\n',
  "function genPartList(pnums){\n",
  "	var l=filePartList.length,r=[],s=parseInt(Math.random()*l);\n",
  '	if(typeof filePartList[pnums]=="number") s=filePartList[pnums]+1;\n',
  "	if(s<l) for(var i=s;i<l;i++) {if(r.length<",
  total,
  ") r.push(filePartArray[i]+'.htm>'+filePartArray[i])};\n",
  "	if(r.length<",
  total,
  "){for(var i=r.length;i<",
  total,
  ";i++) r.push(filePartArray[i]+'.htm>'+filePartArray[i])};\n",
  "	return '<a href=/p/d/'+r.join('</a> | <a href=/p/d/')+'</a>'\n",
  "}\n",
  '<','/script','>\n',
  '<%=genPartList(pnums)%>\n'].join('');

正常的執行結果

<script language="JScript" runat="server">
var filePartList={"086223021101829":0,"14804872":1,"18214103":2,"1N5399TZGW":3,"4308645007":4};
var filePartArray=["086223021101829","14804872","18214103","1N5399TZGW","4308645007"];
function genPartList(pnums){
	var l=filePartList.length,r=[],s=parseInt(Math.random()*l);
	if(typeof filePartList[pnums]=="number") s=filePartList[pnums]+1;
	if(s<l) for(var i=s;i<l;i++) {if(r.length<32) r.push(filePartArray[i]+'.htm>'+filePartArray[i])};
	if(r.length<32){for(var i=r.length;i<32;i++) r.push(filePartArray[i]+'.htm>'+filePartArray[i])};
	return '<a href=/p/d/'+r.join('</a> | <a href=/p/d/')+'</a>'
}
</script>
<%=genPartList(pnums)%>

不正常的執行結果

<script language="JScript" runat="server">
var filePartList={asd^ff好%2f,asd^ff好%2f,"18214103":2,"1N5399TZGW":3,"4308645007":4};
var filePartArray=[-,-,"18214103","1N5399TZGW","4308645007"];
function genPartList(pnums){
	var l=filePartList.length,r=[],s=parseInt(Math.random()*l);
	if(typeof filePartList[pnums]=="number") s=filePartList[pnums]+1;
	if(s<l) for(var i=s;i<l;i++) {if(r.length<32) r.push(filePartArray[i]+'.htm>'+filePartArray[i])};
	if(r.length<32){for(var i=r.length;i<32;i++) r.push(filePartArray[i]+'.htm>'+filePartArray[i])};
	return '<a href=/p/d/'+r.join('</a> | <a href=/p/d/')+'</a>'
}
</script>
<%=genPartList(pnums)%>

根據分析情況是由fileList及fileArray的頭兩個地址被錯誤地更改了。可能是記憶體被佔用了不少時,外面的join會將內裡的join的首兩個地址破壞。
將fileList及fileArray先做好join的運算,再放入原先的join運算,運行一個星期也沒有問題了。 

fileListResult=fileList.join(',');	// calculate the join result first
fileArrayResult=fileArray.join(',');	// calculate the join result first

var sCode=['<',
  'script language="JScript" runat="server"','>\n',
  'var filePartList={',fileListResult,'};\n',
  'var filePartArray=[',fileArrayResult,'];\n',
  "function genPartList(pnums){\n",
  "	var l=filePartList.length,r=[],s=parseInt(Math.random()*l);\n",
  '	if(typeof filePartList[pnums]=="number") s=filePartList[pnums]+1;\n',
  "	if(s<l) for(var i=s;i<l;i++) {if(r.length<",
  total,
  ") r.push(filePartArray[i]+'.htm>'+filePartArray[i])};\n",
  "	if(r.length<",
  total,
  "){for(var i=r.length;i<",
  total,
  ";i++) r.push(filePartArray[i]+'.htm>'+filePartArray[i])};\n",
  "	return '<a href=/p/d/'+r.join('</a> | <a href=/p/d/')+'</a>'\n",
  "}\n",
  '<','/script','>\n',
  '<%=genPartList(pnums)%>\n'].join('');

Read Full Post »