X
782 Rate this article:
No rating

INTERNAL/REVIEW: Using the Build Order of Old IDL Projects in the IDL 7.0 Workbench

Anonym

[Needs to be reviewed for Compliance and IP issues (i.e. .pro file included)]

Topic
The new IDL 7.0 Workbench does not include a view or window equivalent to the 'Build Order' window of IDL Development Environments prior to version 7.0. Build Order in the new Workbench can only be manipulated through a script.

Many, many application projects do not need to have a custom build-order specified. If, however, you are a programmer, who knows that a specific Project that you developed in earlier IDL might be dependent on modifications that you made in the old IDL Projects 'Build Order' window, this Help Article is for you. New IDL Workbench Projects do not have a button you can press to import your old build-order, but the methodology described below can quickly convert build-order information from your old IDL into the build script that the new IDL Workbench wants, and set it up for automated execution.

If you never used the 'Build Order' window in old IDL, and if you are not encountering any error with the application-building done by projects you have imported into new IDL, then you need read no further. The discussion of Build Order in Help Article #4308 ("Tips on Creating New IDL Projects in the IDL 7.0 Workbench") will probably be more relevant to you. If, however, you are not sure whether your old IDL Project used build order, and the project in new IDL is producing errors like the below:

bad_build_order_error.png

then this Help Article might provide the solution for you.

Discussion
Specifying a strict build order for a project can sometimes be essential when IDL '.pro' files contain routines that do not have the same name as the file. Any '.pro' which holds multiple routines is, thus, exposed to the need for a specific build order. Below is an example. If you put these two routines:

    FUNCTION z_function, name
    void = dialog_message('Hello, ' + name + '!', /INFO)
    return, 1
    END

    PRO b_pro
    void = dialog_message('You called B_PRO!', /INFO)
    END

into one file named 'b_pro.pro' (the default name assigned by the 'idlde'), then a fresh new IDL session will have a hard time finding and compiling Z_FUNCTION when it first starts up or .RESET. If a file with this procedure:

    PRO a_pro
    if z_function('John Doe') then $
        void = dialog_message('This application works!', /INFO) $
    else $
        void = dialog_message('Application failed!', /INFO)
    END

is compiled first, then the binary made in that compilation is going to misinterpret the z_function('John Doe') call and cause a future error at runtime. There are ways that an IDL programmer should code 'a_pro.pro' to eliminate this problem (see the Online Help topic "Solving Build Order Problems"), but that is not the focus of this Tech Tip. In this Tech Tip we assume that the original developer solved this problem by specifying a strict build-order in the old IDLDE Projects window. That build-order was stored in the project's '.prj' and the developer would like to migrate that build order specification to IDL 7.0.


IDL 7.0 neither reads the '.prj' files of versions past, nor does it have an equivalent GUI window on which build order can be mouse-interactively modified. The new Eclipse framework for IDL Projects, at least in the Workbench's first release, can only display the '.pro' contents of IDL Projects in alpahabetical order. And, unless there is a custom build script assigned to the project, the Workbench 'Project -> Build Project' menu selection will only build '.pro' files in the order displayed. In our example above, that would mean 'a_pro.pro' would compile every time before 'b_pro.pro'.

Although IDL 7.0 does not have a GUI for modifying build order of a Project's '.pro' source code, it does allow the IDL programmer to specify a 'Build command' build script that can enforce a build-order. The build-order stored in the special-formatted binary '.prj' file is not easy to parse out. However, the copy-and-paste steps detailed below take just a matter of seconds to execute. They create an ASCII build-order list, which can be fed to a simple IDL parser .pro. The one-line call to the parser and its ASCII build-order list can become the permanent 'Build command' for the old IDL project imported into IDL 7.0. (See Help Article #4307 ("Tips on Importing pre-7.0 IDL Projects into the New IDL 7.0 Workbench") for the steps to import old IDL Projects into the new Workbench.)

For this example I added one more '.pro' file to the two mentioned above:

    PRO ex_custom_build_order
    a_pro
    END

stored in 'ex_custom_build_order.pro'. This was intended as the main program file for a project named 'ex_custom_build_order', which should create 'ex_custom_build_order.sav'. The source files were intentionally placed in a subdirectory named 'src' one level under the "application root directory" named "ex_custom_build_order". The '.sav' output of this build will trigger an error in the IDL Virtual Machine, if 'b_pro.pro' does not compile before 'a_pro.pro'.

Here are the steps to establish the specific correct build order:

1) Migrate your old IDL Project to the new IDL Workbench and its 'IDLWorkspace' per Help Article #4307. If your project has always been completely contained in a single application root directory devoted strictly to that project, you will find this step extremely easy.

2) Open and build your old '.prj' -stored IDL Project in any version of the idlde up to version 6.4 using the familiar 'Project->Build' menu option. This creates lines of output in the 'idlde' Output Log that look like this:

    Compiled module: Z_FUNCTION.
    Compiled module: B_PRO.
    Compiled module: A_PRO.
    Compiled module: EX_CUSTOM_BUILD_ORDER.
    Compiled module: RESOLVE_ALL

The order of compilation above reflects the build order that is specified in the 'Build Order' tab page of your old IDL.

3) From this output it is a simple matter to highlight and copy the "Compiled module: ..." lines that are relevant to your project build. Paste these into a simple ASCII text editor (e.g. Windows Notepad, Mac 'xedit', UNIX 'emacs' or 'vi'). Before you save the file, make sure it has nothing but lines that begin with "Compiled module: ".

4) Save this ASCII text file to your application root directory, preferably with the naming convention "[proj_name]_build_list.txt".

5) The 'Example Code' section below shows the parser that can parse this build list file and compile the IDL routines named there in the order they appear in the file. You can download your own copy of this file at this link: compile_build_list.pro. Save this to any directory in your persistent IDL Search Path where you store frequently-used helper routines. (I store mine in '~/IDLWorkspace/utilities/').

6) Below is how this tech tip's example project might now appear in the IDL 7.0 Project Explorer. The '.prj' file from old IDL is automatically visible in new IDL, even though it is not directly used. The configuration file that will be used by COMPILE_BUILD_LIST, 'ex_custom_build_order_build_list.txt', is also right in view. Right-click on that file, select 'Properties', highlight and copy the 'Location' label shown in the properties dialog. 'Cancel' out of this dialog. Now the file path is easy to paste in step 7 below.

tt4306_project_explorer.png

7) Highlight the project name in the Project Explorer, right-click and select 'Properties' from its context menu. Select the 'IDL Project Properties' page.

8) 'Use a custom build command' is the only setting that has to be modified, but we would recommend checking the option to 'Execute .RESET_SESSION' as well. The build command will always be a combination of "COMPILE_BUILD_LIST, " the path you copied in step 6 above, wrapped in quotes, and a closing call to RESOLVE_ALL, as shown below (though all on one line in your dialog):

    COMPILE_BUILD_LIST, 'C:\Documents and Settings\aeinstein\idl_programs\
    ex_custom_build_order\ex_custom_build_order_build_list.txt' & RESOLVE_ALL, /CONTINUE_ON_ERROR

tt4306_project_properties.png'

Hit 'Apply' and 'OK'.

The next time you right-click on the project folder and select 'Build Project' from the context menu, it should produce output like this in your Workbench Console:

    *** Build Project: 'ex_custom_build_order'
    *** Running build command: COMPILE_BUILD_LIST, 'C:\Documents and Settings\aeinstein\idl_programs\
    ex_custom_build_order\ex_custom_build_order_build_list.txt'
    % Compiled module: COMPILE_BUILD_LIST.
    % Compiled module: STRSPLIT.
    Could not find Z_FUNCTION in its own .pro file
    % Compiled module: B_PRO.
    % Compiled module: A_PRO.
    % Compiled module: EX_CUSTOM_BUILD_ORDER.
    *** Creating save file...
    *** Save File At: C:\Documents and Settings\jjones\My Documents\CodeExamples\ex_custom_build_order\ex_custom_build_order.sav
    *** Build complete: Time = 3.047s

(Note: Although the new IDL Workbench does not verbosely show the compilation of routines that do not have their own same-named '.pro' files, like Z_FUNCTION, a call to "IDL> HELP, /ROUTINES" will confirm that it was indeed among the routines compiled in the above build.)


Solution:

; File: compile_build_list.pro 
; Syntax: COMPILE_BUILD_LIST, '/path/to/optimal_build_order.txt'

; Instructions: If the '.pro's of a given IDL 7.0 project are in need of

; being compiled in a specific sequence, use this procedure with an
; ASCII text build list file in the 'custom build command' property of ; an IDL 7.0 Project.
; Purpose: Specifically designed to parse lists of "Compiled module: ..."

; statements that have been captured in the Output Log of old (pre-7.0)

; IDL DE's. After parsing out each routine name, this procedure feeds it

; to the IDL compiler procedure RESOLVE_ROUTINE. The 'buildListFile' file
; is expected to have its listed routines sequenced in optimal build
; order.

; This parser expects 'buildListFile' to be an ASCII text file with lines

; that have exactly this structure:

;

; Compiled module: B_PRO.
; Compiled module: A_PRO. ; ... etc.
;

; A file made up of nothing but IDL routine names, one per line, will

; also work with this parser and compiler, but see the Online Help

; article 'IDL Workbench Guide -> Tasks -> Running and Building IDL

; Projects -> Build Commands -> Use a Custom Build Command' for

; better options for build lists that you compose from scratch.


PRO
compile_build_list, buildListFile text = strarr(file_lines(buildListFile))
; routine steps to import ASCII text from a file into an IDL array

openr
, lun, buildListFile, /GET_LUN
readf
, lun, text free_lun, lun
; In a loop, parse out the routine name from each text line, then pass
; it to RESOLVE_ROUTINE. The CATCH statement is needed to handle a
; normal RESOLVE_ROUTINE error message.

for
i = 0, n_elements(text)-1 do begin
; Get rid of the 'Compiled module: ' prefix, if it's there
currentLine = strsplit(text[i], 'Compiled module: ', /REGEX, /EXTRACT)
; Get rid of the closing period, if it's there
routineName = (strsplit(currentLine[0], '.', /EXTRACT))[0] catch, error_code
if
error_code ne 0 then begin print, 'Could not find ' + routineName + ' in its own .pro file' catch, /CANCEL
; Deliberately handle one specific RESOLVE_ROUTINE error ...
if !error_state.name eq 'IDL_M_UPRO_UNDEF' then continue
print
, !error_state.msg
; ... else report any other error
endif else begin
resolve_routine
, routineName, /EITHER, /COMPILE_FULL_FILE, $  /NO_RECOMPILE endelse endfor END