Feeds:
Posts
Comments

Archive for the ‘Script’ Category

Bar Model Maker

I am developing a Windows desktop application, called Bar Model Maker, that allows you to draw Bar Models by writing Bar Model Script. The tool displays the bar model as you are writing the script. Once you’ve specified enough constraints, you will get the accurate bar model.

The user interface will be something as the following screen shot. Beginners can create a bar model by using the dialog boxes on the right. Advanced users can write script directly in the script editor.

Bar Model Maker

 

Advertisements

Read Full Post »

I used to use C# to write small tools to automate some of my development tasks. Lately, I find myself writing more and more PowerShell scripts. For certain daily tasks, it is much more productive to write PowerShell scripts than C#. (PowerShell is built on top of .NET, making the entire .NET framework available for you to use. Sometimes, I use PowerShell to explore how to use a .NET class, or a 3rd party API.)

The following script, makefile.ps1, shows how to find all .cpp files in nested folders and create a make file. The script, taking one parameter to specify the folder where the .cpp files to be found, can be invoked something as follows.

PS D:\MyProjects>.\makefile.ps1 .\Project1

The script first defines two functions: doFile and doFolder. The doFile function gets the full name of the file, changes its extension to ‘o’ from ‘cpp’, gets the relative file path, which is then appended to the StringBuilder object.

The doFolder function goes to the given folder, finds all .cpp files in that folder, and for each of them, invokes the doFile function. It then finds out all sub-folders, and invokes the doFolder function for each of them recursively. The {$_.Mode -match “d”} script block is to check if a given child item is a directory.

When the script is executed, it first goes to the folder if it is specified. It then gets the current location and assigns it to $currFolder, creates and instances of StringBuilder and invokes the doFolder function by passing in $currFolder and the StringBuilder object. It then appends other necessary information to the StringBuilder object, and finally writes the string to a file.

$currFolder = $null

function doFile($file, $sb)
{
    $fileName = $file.FullName
    $fileName = [System.IO.Path]::ChangeExtension($fileName, "o")

    [void] $sb.Append("`t")
    $fileName = $fileName.SubString($currFolder.Length + 1)
    [void] $sb.Append($fileName)
    [void] $sb.Append(" \`n")
}

function doFolder($folder, $sb)
{
    write-host "Processing folder: ""$folder""..."

    cd $folder

    # Invoke doFile for each .cpp file.
    get-childitem *.cpp | foreach-object { doFile $_ $sb }

    # Invoke doFolder recursively for each sub-folder
    get-childitem * | where-object {$_.PSIsContainer} | foreach-object { doFolder $_ $sb }

    cd ..
}

if ($args -ne $null -and $args[0] -ne $null)
{
    cd $args[0]
}

$currFolder = get-location
$currFolder = $currFolder.Path

$contentBuilder = new-object System.Text.StringBuilder
[void] $contentBuilder.Append("OUTFILES =")

doFolder $currFolder $contentBuilder

[void] $contentBuilder.Remove($contentBuilder.Length - 2, 1)
[void] $contentBuilder.Append("`nall: `$(OUTFILES)`n")
[void] $contentBuilder.Append("`nclean:`n")
[void] $contentBuilder.Append("`tcmd.exe /c del /s /q *.o`n")
[void] $contentBuilder.Append("`n%.o: %.cpp`n")
[void] $contentBuilder.Append("`gcc -o $@ $<`n")

cd $currFolder
set-content -encoding ascii makefile $contentBuilder.ToString()

You may ask: “Why do we need all those [void] type castings in the script?” Yes, they are there to discard the output of the statement. Otherwise you will have something like the following in the output.

Capacity                MaxCapacity                    Length
--------                -----------                    ------
      16                 2147483647                         1
      16                 2147483647                         2
      16                 2147483647                         3

For details, please read this blog entry, http://keithhill.spaces.live.com/Blog/cns!5A8D2641E0963A97!811.entry

PowerShell provides many built-in cmdlets that can be pipelined together, allowing you to write compact scripts. Using C#, you programming rhythm is edit-compile-run. With PowerShell, you just type and run, making you more productive. And the powerful reflection service provided by PowerShell, such as Get-Member, makes it a good tool for learning .NET.

Read Full Post »

I am developing a tool in C# with Visual Studio 2008. There are two C# files that are generated based on some data files, which are also used at runtime. There are multiple sets of the data files, each of them stored in in different folder. For different purpose, I need to use the data files from different folder. A data file, once defined, will never be changed or removed. When a data file needs to be changed, a new revision of it will be created and co-exist with the older ones. So if the number of files of two folders are different, we can safely say that the two sets of data files are different.

So, how can we automate the build process of the project?

First, I create an environment variable, XXX_DataFiles, indicating the folder of the data files. We can then use this variable in the project settings. When we want to use the data files from another folder, we can simply change the value of the environment variable, without having to change the project settings.

In the Build Event tab of the project properties dialog, I add the script something as follows:

echo OFF

IF NOT EXIST "$(SolutionDir)$(ProjectName)\A.cs" GOTO generate
IF NOT EXIST "$(SolutionDir)$(ProjectName)\B.cs" GOTO generate

for /F %%j in ('dir /A /B "$(TargetDir)Data\*.dat" ^| find /C /V ""') do set Count1=%%j

for /F %%j in ('dir /A /B "$(XXX_DataFiles)*.dat" ^| find /C /V ""') do set Count2=%%j

if %Count1% == %Count2% GOTO end

:generate

IF EXIST "$(TargetDir)Data" GOTO start_del

mkdir  "$(TargetDir)Data"
GOTO start_copy

:start_del
del /Q "$(TargetDir)Data\*.*"

:start_copy
xcopy "$(XXX_DataFiles)*.dat" "$(TargetDir)Data" /Q

MyCodeGenerator "$(TargetDir)Data" "$(SolutionDir)$(ProjectName)"

:end

First, if any of the generated C# files doesn’t exist, we go ahead to the generate. If both of them exist, we check whether the target folder and source folder of the data files have the same number of files. If the numbers are the same, we terminate the process. Otherwise, we create the target folder if it doesn’t exist yet and start copy the files. If the target folder exists, we delete all files inside the folder and copy data files from the source folder. Finally we invoke the MyCodeGenerator to generate the two C# files.

The script

for /F %%j in ('dir /A /B "$(TargetDir)Data\*.dat" ^| find /C /V ""') do set Count1=%%j

finds the number of .dat files in the “$(TargetDir)Data” folder and saves the number in the Count1 environment variable.

Read Full Post »