Documention for: build-pack.r Created by: btiffin on: 29-Jun-2007 Last updated by: btiffin on: 29-Jun-2007 Format: text/editable Downloaded on: 28-Mar-2024 Using %build-pack.r Source Code by Carl Sassenrath, REBOL Technologies Usage document by Brian Tiffin 28-Jun-2007 ===Introduction to %build-pack.r This script creates a single compressed REBOL program from a list of files. File can include binary data files such as images. The output from build-pack.r is plain text so it can be opened in any text editor or sent as an email message. ===Using %build-pack.r build-pack.r is an example of a rebol.org repositoryHow-To. That means that it shows how to do something useful, but may require changes for it to do something useful for you. build-pack.r is a tool to help you bundle up a bunch of files into a single executable REBOL file. In particular, this can let you include images and other data that a program may need, right in the program package. ---The files list You will need to change the list of files to include in the package you want to build. The list is held in block. You will see, near line 28ish files: [ logo: %nyc.jpg %show.r ] the example file list. You will need to change this for your own purposes, and this list has some special features. Data is included in the package by placing a REBOL variable name in front of the filename. From the list above, the logo: %nyc.jpg will include the image %nyc.jpg in the bundle and the %show.r script will be programmed to use the logo variable. ===Building a test package Before we change %build-pack.r, let's setup a test. From the code above, we will need two files: #%nyc.jpg #%show.r As this is an example How-To, there is no %show.r program. So, lets build our own. ---Setting it up Starting from a working directory, lets say /home/blue/rebol/test for GNU/Linux, or maybe C:\blue\rebol\test for Windows™. Of it could be some longer filename such as C:\Documents and Settings\blue\rebol\test Just so you know, I use Debian GNU/Linux and my computer user name is blue (my friends all call me Bluey). If you become involved with the REBOL community you may see me online as btiffin as well. So the blue in the above example would be changed to your username. If you don't use a Windows user name, it may be Administrator. As a programmer, it may be easier to create a directory off of your C drive, just to get sane filenames, or be prepared to get used to the Windows™ directory structure. ---Some assumptions For the sanity of this document, I'll use :home directory - /home/blue :system shell - bash with a prompt of $ :rebol - REBOL/View 1.3.2 with a console prompt of >> So lets start up REBOL/View and get to work. You may need to press the Console icon (looks like a computer terminal) on the left of REBOL/View desktop when it starts up. I have my REBOL/View configured to start in Console mode. $ cd /home/blue $ rebol >> make-dir/deep %rebol/test/ ---A minimal %show.r >> write %show.r {REBOL [] view layout [image (load logo)]} that gives a %show.r of REBOL [] view layout [image (load logo)] That is all it takes. After we get a copy of %nyc.jpg, we can build the package. The above %show.r is an example of a minimal REBOL program. REBOL lets you build more information into a program, and it is a lot more professional and friendly if you get used to including at least a little documentation in your programs. So let's do a slightly longer, but more complete %show.r instead. ---A less minimal %show.r You might want to use the REBOL/View editor for this >> editor %show.r or any editor you like, but save it to your working directory as show.r REBOL [ Title: "Show New York city" File: %show.r Author: "Brian Tiffin" Date: 28-Jun-2007 Purpose: "An example program for build-pack.r" ] ;; This code assumes it has access to a variable called "logo" ;; It does when included in the build-pack example %testpack.r view layout [ vh1 "New York city" image (load logo) btn "Close" [unview] ] Once again, that's all it takes. This time we added a documented REBOL header, a title above the picture and a close button. I used the builtin REBOL/View editor function, but you could easily use any editor you like. ---Getting %nyc.jpg A lot of the How-To scripts include a small set of standard images. These images are stored on the rebol.com website. We can get a copy of the New York city picture with >> write/binary %nyc.jpg read/binary http://www.rebol.com/view/nyc.jpg So now we have %show.r and %nyc.jpg, let's run build-pack.r. We could actually do this right out of the library since we've set things up so we don't need to change the packaging script, but let's make a local copy. >> write %build-pack.r read http://www.rebol.org/library/scripts/build-pack.r >> do %build-pack.r This will pop up a save file requestor, titled "Save file as:" Let's use testpack.r as the output name. So answer the Save file as: with testpack.r and hit the Select button. (Under REBOL/View Linux, the button is labelled Select, it may be different for your operating system.) Now let's run it. >> do %testpack.r And, unless something was typed wrong...not totally unlikely, I do it all the time...you should see a window with a nice picture of New York city, and a close button underneath. ===What you can learn The rest of this document is a kind of line by line analysis of the script. Explaining things as it goes. One of the nice things about REBOL is that programs can be pretty short for amount of functionality that is involved. ---Filenames in REBOL Note: REBOL uses % (percent) before a filename. This is a builtin datatype. The REBOL scanner uses the %nyc.jpg syntax to "know", deep down "know", that you mean the file nyc.jpg. This may seem a little strange at first, but you'll be slinging filenames around using the percent syntax, before you know it. ---system/options/binary-base The REBOL system object has an internal variable that controls the numeric base for binary! molding and conversion. When you look at %testpack.r you will see that it start out with code: 64#{ which tells REBOL to expect a base 64 binary string. Base 64 lets things compress down further than base 16, the normal mode of hexadecimal operation. Base 64, packs more binary! into the string version as it can use more symbols to represent values. Common values for system/options/binary-base are 64, 16, and 2. The default of 16 is usually fine, unless you really want to shrink down included string forms of binary data. Other library scripts, %bin-data.r and %bin-save.r use these features as well. ---Defining functions One of the first things this script does is define an error function. REBOL functions are defined with a header and a body. The header defines the parameters and the body is what executes. The REBOL/Core user manual explains this in detail in Chapter 9. REBOL functions can be highly self documenting, but the error function defined here is not. Not a bad thing per say, just not self documented in this case. ---make things One of the nice features of REBOL is make. Make, makes things. It can be used for simple preallocation of string space, all the way up to building complex objects In this script, the variable out is preallocated with 20,000 bytes of space. This is solely for performance. REBOL will allocate and garbage collect things on the fly, with no worries from the programmer's perspective. ---right to left flow through assignment Next this script initialises two variables, variable and main both to the special data value none. none is a special data value used throughout REBOL, and you'll get used to it quickly. It doesn't look like much but it enables all kinds of expressive power and shortcuts in source code. ---foreach Now we get to some meaty REBOL code. Put on the seat belt, we're going for a loop. foreach file files starts a looping structure that sets the local variable file to every element of the files block. And this loop starts out with some what the? kind of REBOL code. ---either either is the REBOL version of if/then/else found in other programming languages. If the test is true evaluate the first block, and if it is not true then execute the second block. In this case, the script is testing to see if the file variable is actually a word, logo: in this sample, but it could be any word. ---any-word? any-word? tests a value for literal words, set-words, get-words and a few other things in the REBOL realm of words. In the case of this script it expects a set-word. A set-word is the REBOL way of assigning things to variables, or, a name followed immediately by a colon. (logo:) There is no "=" or "==" syntax in REBOL. REBOL uses set-words to umm, set words. So, following into the either, if the value is an any-word, then set the variable, variable to the value of the value. Hmmm, value of the value? That is the special colon immediately followed by a name or a get-word. It means don't do the normal variable evaluation, but return the value of the variable instead. ---get-word :file tells REBOL that you want the unevaluated value of the word. As an example print [1 2 3] will print out the values 1 2 3. If you said myvar: print REBOL would attempt to evaluate print and then set the result to myvar. But what if you wanted the 'function' that is print? That's where get-words come in. myvar: :print returns the value that is print, not the results of print. So now you could write code like myvar [1 2 3] and it will print out the values 1 2 3. In this loop, it lets REBOL test for things like logo: without triggering the assignment of logo. And then it goes a little further. to-word :file when file contains logo: returns the word logo without the colon. It converts the set-word to a word. ---Careful when you are eyeballing code. The either sets variable to a variable name or assumes you want to include a file. Sometimes you have to be careful looking at REBOL code, because indentation is only for human benefit, the computer doesn't care. In this example, there is no indentation for the else part of the condition test. It is on the same line. If :file is any-word, set variable, otherwise the false block, which starts on the same source line is evaluated, so when scanning this code quickly, the first indented bit is not the true case, but the false case. ---exists? So, assuming we want a file, the next line checks to see if the file exists. If not, error out and quit the program, this is treated as an unrecoverable error and you'll either have to update the files block or move the file around to the right directory. In this case %nyc.jpg and %show.r both have to exist in the current directory. ---script? We passed the file exists test, so now we test if the mentioned file is a REBOL script. REBOL scripts all start with a REBOL header block. It can be empty, but it has to be there. script? tests a file to see if it has a header. Using either, a file has a header or it doesn't. If it does, we execute a really cool piece of REBOL code. ---load/all One of the more fascinating features of REBOL is the ability to load and save. Try and experiment with these features. It takes a while to get used to, but load/all can be a very powerful programming technique. load converts things into a block of REBOL values. load/all loads an entire script into a block, with the header portion as the first element and the body (or whatever follows) as the next part(s). ---none testing Remember the right to left assignment of none we did a few lines back? If main is none that means we have not set any real value into it yet. The first script in the files block is stuffed into main. Any other scripts, main won't be none anymore, so this assignment will be skipped. ---append The string out that was preallocated before the loop started now has the contents of script appended to the end of it. Well, actually it is the molded value of the script, without any REBOL header information, that is the skip 2 part. The output package has it's own header information added, so the input script header is ignored. ---mold REBOL can produce a string form of values that REBOL can understand. Mold is the way to go for this. Strings are properly quoted, blocks are properly umm, blocked and binary values are prepped in a way that they can be read back in. ---tricks for set-words If the file is not a script, the code now checks to see if there was an any-word stashed away in variable. If there was, it is output along with a string colon ":" and when REBOL evaluates the package bundle, it will look to REBOL like a set-word, exactly what we want for the logo variable associated with the %nyc.jpg file. REBOL can build programs from internal REBOL values, or you can build code out of strings. After the set-word trick, the associated file is treated as binary information and appended to the output bundle as a molded binary string. The code then loops back to the foreach, sets the local file variable to the next item in the files block and all the tests and out appending are done again. ---Testing for at least one script If after the files loop, there is no main, %build-pack.r will display an error message and quit. No sense building a package if there is nothing to execute. ===Compression If there is a main, then the current output is compressed. A function built into REBOL is data compression. The variable out is replaced with a compressed version of itself. ---Adding a header The package now gets a REBOL header block. +++reform Reform is short for reduce form, which takes a block of information, reduces it to REBOL values and forms it into strings. +++'REBOL literal When you reduce a value, it is evaluated. The package bundle wants to start with the word REBOL. The expression 'REBOL tells REBOL to use the literal word REBOL, and to not try and evaluate it as a variable. This is another type of REBOL word, a lit-word, or literally the word that follows the apostrophe. ===make and context and other goodness Now we get to a pretty hairy expression. mold third make context main/2. The mold we've seen before, it creates a string that REBOL can read. +++third Third returns the third element of a series. We'll get back to this. +++make The make is going to create an object in the case, we'll come back to this too. +++context Context is a shortform for make object! [...stuff...], and we'll get back to this in a sec. +++main/2 REBOL allows for path notation. You can get at object fields and other things, but in this case, it returns the second element of the main block. It turns out that the first element of main is the keyword REBOL and the second part is the header information we included in the %show.r program. Repeated here. [ Title: "Show New York city" File: %show.r Author: "Brian Tiffin" Date: 28-Jun-2007 Purpose: "An example program for build-pack.r" ] So, this last sequence makes an object! with context with the header fields, takes that object and adds a few fields [ Built: now Length: length? out ] now being the current data and time, and length? returning the length of the compressed out variable, and creates a new object (the make before the context) so we get and extended object. ---third of an object. It turns out that the third field of an object in REBOL is kind of the source code for that object. REBOL has reflective properties for just about everything. So the mold third make context main/2 creates source code of an extended header object that started out in the main script. Pheww... This information is stashed away in the header variable along with the literal REBOL word. ---repend repend is short for reduce append. What follows is the actual code that the package bundled will execute when you run %testpack.r, or any of the other sophisticated world class REBOL code bundles you craft together in the future. ---Another set-word trick The bundle starts out with a blank line after the REBOL header block, (a reduced newline is a blank line when saved to a text file), followed by a set-word, code: followed by a bunch of compressed data/code/code/data stuff that we built into the out string. After this, is the self executing bundle will verify that nothing has gone wrong with itself by making sure the length of the code is what the %build-pack.r builder thought it was, and if everything checks out, decompresses all the scripts and data we stuffed in the bundle and then evaluates it using do. The script that this will execute first is the one that ended up in the main variable, the first script in our files list, or in this case the code that was in the %show.r file. ===You made a bundle So now you can build packages out of scripts that need data associated with them and deliver that package to the world as a single text editor friendly REBOL executable file. Feel the power. ===Credits \table File Author =row %build-pack.r Carl Sassenrath =row buil-pack Usage doc Brian Tiffin /table Credit also to the rebol.org library team for keeping the repository up and running.