Euphoria Ticket #324: Gather include files

We need a utility to gather all of the files used by a program. At minimum, generate a list, but even better would be to pull them all together into a directory, along with subdirectories as appropriate. It would probably be useful to exclude the std library by default, and have the ability to exclude certain files as directed by the user.

This would make distributing programs with all of their dependencies much easier, as well as submitting code for bug reports.

As for subdirectories, if the program did:

-- myapp.ex 
include foo/bar.e 

...then the utility would go something like:

$ eudist myapp.ex 
Euphoria distribution helper v1.0 
Outputting files to directory: myapp 
myapp/myapp.ex 
myapp/foo/bar.e 
...or something similar. Basically, we should be able to distribute that directory, and someone else (with a functional euphoria install) should be able to run it.

We'd probably also need a way to specify other resources that need to be included.

Details

Type: Feature Request Severity: Normal Category: Bundled Utility
Assigned To: jimcbrown Status: Fixed Reported Release:
Fixed in SVN #: 3860, 3880 View VCS: 3860, 3880 Milestone: 4.0.0RC1

1. Comment by euphoric Nov 05, 2010

Tone Skoda did something like this. See here ("Copy Included Files").

2. Comment by jimcbrown Nov 05, 2010

I have a working parser that will print the required files.

It still needs some polishing.

--JBrown 
--combine.ex 
--global scopes aren't redefined 
    --this MIGHT cause an error, however, euphoria should normally have an 
    --error in that case too. 
--procedure-scope vars aren't saved, this makes code more readable 
    --N/A if shroud is on 
--you can either choose not to combine files in a list, or 
    --you can ignore include files on 1 dir (default: eu's include dir) 
--shrouding symbols is optional. 
--bug fixed: local symbols were being treated as global. 
--bug fixed: strings with \x were changed to \x\x. 
--bug fixed: file c:\myfile.e and myfile.e were the diffrent files. 
--added platform to sequence builtin 
--bug: using routine_id("abort_") gives error 
    --"Unable to resolve routine id for abort" 
    --to fix made routine_id-ing of builtin routines legal. 
-- concat.ex 
-- version 1.0 
-- 
-- replacement for Euphoria's bind routine 
 
include std/get.e 
include std/io.e 
include std/text.e 
include std/console.e 
include std/sequence.e 
include std/filesys.e 
 
----------------------------------------------------------------------------- 
-- code from RDS's ED color coding routines 
 
sequence charClass 
 
-- character classes 
constant     
    DIGIT       = 1, 
    OTHER       = 2, 
    LETTER      = 3, 
    BRACKET     = 4, 
    QUOTE       = 5, 
    DASH        = 6, 
    WHITE_SPACE = 7, 
    NEW_LINE    = 8 
 
    charClass = repeat( OTHER, 255 ) 
 
     
    charClass['a'..'z'] = LETTER 
    charClass['A'..'Z'] = LETTER 
    charClass['_']      = LETTER 
    charClass['0'..'9'] = DIGIT 
    charClass['[']      = BRACKET 
    charClass[']']      = BRACKET 
    charClass['(']      = BRACKET 
    charClass[')']      = BRACKET 
    charClass['{']      = BRACKET 
    charClass['}']      = BRACKET 
    charClass['\'']     = QUOTE 
    charClass['"']      = QUOTE 
    charClass[' ']      = WHITE_SPACE 
    charClass['\t']     = WHITE_SPACE 
    charClass['\n']     = NEW_LINE 
    charClass['-']      = DASH 
 
----------------------------------------------------------------------------- 
constant 
stdlib40 = { 
"include/std/eds.e", 
"include/std/pipeio.e", 
"include/std/pretty.e", 
"include/std/math.e", 
"include/std/io.e", 
"include/std/datetime.e", 
"include/std/mathcons.e", 
"include/std/localeconv.e", 
"include/std/cmdline.e", 
"include/std/error.e", 
"include/std/image.e", 
"include/std/rand.e", 
"include/std/flags.e", 
"include/std/primes.e", 
"include/std/types.e", 
"include/std/filesys.e", 
"include/std/sort.e", 
"include/std/memconst.e", 
"include/std/mouse.e", 
"include/std/locale.e", 
"include/std/os.e", 
"include/std/regex.e", 
"include/std/stack.e", 
"include/std/wildcard.e", 
"include/std/convert.e", 
"include/std/win32/sounds.e", 
"include/std/win32/msgbox.e", 
"include/std/eumem.e", 
"include/std/unittest.e", 
"include/std/sequence.e", 
"include/std/task.e", 
"include/std/memory.e", 
"include/std/stats.e", 
"include/std/serialize.e", 
"include/std/graphcst.e", 
"include/std/console.e", 
"include/std/map.e", 
"include/std/debug.log", 
"include/std/text.e", 
"include/std/dll.e", 
"include/std/utils.e", 
"include/std/safe.e", 
"include/std/lcid.e", 
"include/std/socket.e", 
"include/std/graphics.e", 
"include/std/get.e", 
"include/std/machine.e", 
"include/std/net/dns.e", 
"include/std/net/http.e", 
"include/std/net/url.e", 
"include/std/net/common.e", 
"include/std/search.e" 
}, 
oldstdlib = { 
"include/image.e", 
"include/file.e", 
"include/sort.e", 
"include/mouse.e", 
"include/msgbox.e", 
"include/wildcard.e", 
"include/misc.e", 
"include/database.e", 
"include/dll.e", 
"include/safe.e", 
"include/graphics.e", 
"include/get.e", 
"include/machine.e" 
}, 
euphorialib = { 
"include/euphoria/stddebug.e", 
"include/euphoria/keywords.e", 
"include/euphoria/syncolor.e", 
"include/euphoria/tokenize.e", 
"include/euphoria/info.e" 
} 
 
sequence 
    included, 
    includedNewNames, 
    excludedIncludes 
 
    included = {} 
    includedNewNames = {} 
    excludedIncludes = {} 
 
object outputDir=-1 
 
constant 
    EuPlace = getenv( "EUDIR" ) 
    --Place = { "", EuPlace & "\\", EuPlace & "\\INCLUDE\\" } 
sequence Place = { current_dir()&SLASH, EuPlace & SLASH, EuPlace & SLASH&"include"&SLASH, "" }, 
mainPath = "" 
----------------------------------------------------------------------------- 
function findFile( sequence fName ) 
 
    -- returns where a file is 
    -- looks in the usual places 
     
    -- look in the usual places 
    --trace(1) 
    if find(fName[length(fName)], {10, 13}) then 
	fName = fName[1..length(fName)-1] 
    end if 
    for i = 1 to length( Place ) do 
	if sequence( dir( Place[i] & fName ) ) then 
	    if platform() = 3 then 
		return Place[i] & fName 
	    else 
		return upper( Place[i] & fName ) 
	    end if 
	end if 
    end for 
     
    printf( 1, "Unable to locate file %s.\n", {fName} ) 
    --abort(0) 
     
end function 
 
----------------------------------------------------------------------------- 
function getIncludeName( sequence data ) 
 
    -- if the statement is an include statement, return the file name 
    integer at 
     
    -- include statement missing? 
    if not match( "include ", data ) then 
	return {"","",""} 
    end if 
 
    -- trim white space 
    while charClass[ data[1] ] = WHITE_SPACE do 
	data = data[2..length( data ) ] 
    end while       
     
    -- line feed? 
    if find( '\n', data ) then 
	data = data[1..length(data)-1] 
    end if 
    if find( '\r', data ) then 
	data = data[1..length(data)-1] 
    end if 
 
    sequence includeType 
    -- not first statement? 
    if equal( data[1..8], "include " ) then 
	-- remove statement 
	includeType = data[1..8] 
	data = data[9..length(data)] 
    elsif length(data) > 15 and equal( data[1..15], "public include " ) then 
	-- remove statement 
	includeType = data[1..15] 
	data = data[16..length(data)] 
    else 
	-- not an include statement 
	return {"","",""} 
    end if 
 
    sequence nameSpace = "" 
    -- remove data after space 
    at = find( ' ', data ) 
    if at then 
	nameSpace = data[at..$] 
	data = data[1..at-1] 
    end if 
 
    return {data,nameSpace,includeType} 
 
end function 
 
 
----------------------------------------------------------------------------- 
function trimer(sequence s) 
    sequence t 
    integer u 
    if s[length(s)] = '\n' then 
	s = s[1..length(s)-1] 
    end if 
    if s[length(s)] = '\r' then 
	s = s[1..length(s)-1] 
    end if 
    --t = reverse(s) 
    --u = find(SLASH, t) 
    --if not u then 
	--return s 
    --end if 
    --t = t[1..u-1] 
    --s = reverse(t) 
    return s 
end function 
 
function includable(sequence name) 
    return not find(name, excludedIncludes) 
end function 
 
----------------------------------------------------------------------------- 
without warning 
function parseFile( sequence fName ) 
 
    integer inFile, outFile 
    sequence newIncludeName, includeName, newfName, nameSpace, includeType 
    object data 
 
	included = append( included, fName ) 
 
    -- find the file 
    fName = findFile( fName ) 
     
    inFile = open( fName, "r" ) 
    newfName = filename(fName) 
 
    if sequence(outputDir) then 
   	 while file_exists( outputDir & SLASH & newfName ) do 
	 	newfName &= sprintf("%d", rand(10)) 
	 end while 
   	 outFile = open( outputDir & SLASH & newfName, "w" ) 
    else 
	 outFile = -1 
    end if 
 
	includedNewNames = append( includedNewNames, newfName ) 
     
    while 1 do         
     
	-- read a line 
	data = gets( inFile ) 
     
	-- end of file? 
	if integer( data ) then 
	    exit 
	end if 
 
	-- include file? 
	includeName = getIncludeName( data ) 
	includeType = includeName[3] 
	nameSpace = includeName[2] 
	includeName = includeName[1] 
	if length( includeName ) and includable(trimer(includeName)) then 
	 
    -- already part of the file? 
    if find( includeName, included ) then 
	--return 
	    newIncludeName = includedNewNames[find(includeName, included)] 
    else 
	    -- include the file 
	    newIncludeName = parseFile( includeName ) 
    end if   
    if outFile != -1 then 
	    puts( outFile, includeType & newIncludeName & nameSpace & "\n") 
    end if 
	     
	else 
    if outFile != -1 then 
	    puts( outFile, data ) 
    end if 
	end if 
	 
    end while 
     
    close( inFile ) 
    if outFile != -1 then 
    close( outFile ) 
    end if 
    return newfName 
     
end function 
with warning 
 
function getListOfFiles(sequence dir) 
	-- TODO implement XXX 
	return "" 
end function 
 
----------------------------------------------------------------------------- 
     
procedure run()    
    object cmd, inFileName 
    inFileName = -1 
 
    -- read the command line 
    cmd = command_line() 
 
    for i = 3 to length(cmd) do 
    	if equal(cmd[i], "-i") then 
		if i = length(cmd) then 
			puts(1, "Expected filename to follow -i!\n") 
			abort(0) 
		else 
			inFileName = cmd[i+1] 
		end if 
    	elsif equal(cmd[i], "-d") then 
		if i = length(cmd) then 
			puts(1, "Expected output dir to follow -d!\n") 
			abort(0) 
		else 
			outputDir = cmd[i+1] 
		end if 
    	elsif equal(cmd[i], "-e") then 
		if i = length(cmd) then 
			puts(1, "Expected excluded include file to follow -e!\n") 
			abort(0) 
		else 
			excludedIncludes = append(excludedIncludes, cmd[i+1]) 
		end if 
    	elsif equal(cmd[i], "-ed") then 
		if i = length(cmd) then 
			puts(1, "Expected excluded include dir to follow -ed!\n") 
			abort(0) 
		else 
			excludedIncludes &= getListOfFiles(cmd[i+1]) 
		end if 
    	elsif equal(cmd[i], "--no-copy") or 
    	      equal(cmd[i], "-nc") then 
	      	outputDir = -1 
	end if 
    end for 
 
    -- get input file 
    if atom(inFileName) then 
	inFileName = prompt_string( "File to parse? " ) 
	if length( inFileName ) = 0 then 
	    abort(0) 
	end if 
    end if 
		      
    mainPath = pathname(canonical_path(inFileName)) 
    Place &= {mainPath&SLASH} 
    -- process the input file 
    parseFile( inFileName ) 
 
    printf(1, "%d files were found. These are:\n", {length(included)}) 
    for i = 1 to length(included) do 
    	printf(1, "%s\n", {included[i]}) 
    end for 
		        
end procedure 
 
run() 

3. Comment by jeremy Nov 05, 2010

It would be nice if our bundled utilities all used our new std/cmdline.e so that our bundled utilities have a common feel/operation to them. This will also provide -help support automatically which should be included with any bundled utility.

4. Comment by jimcbrown Nov 05, 2010

Agreed. I'll assign that task to jimcbrown immediately.

5. Comment by DerekParnell Nov 06, 2010

Moved to RC1

6. Comment by jimcbrown Nov 06, 2010

I'm done working on this and marking it as fixed.

What it does not do:

Automatically exclude the stdlib. Right now you can manually exclude the stdlib dir just like any other directory. Also, it's not clear what should be excluded - just the std directory? All of include? What about include/euphoria? Finally, there are cases where you'll want the user to include the version of the stdlib that they are using (in case it's a very old version no longer easily obtainable, or one that might have local modifications, for example).

Copy subdirectory structure. All files are copied flat, but renamed if there is a conflict with an earlier file. I like having a flat option, but otherwise don't have an objection with this feature. It's just more effort than it's worth.

Does not support using eu.cfg to specify include locations. Only EUINC/EUDIR and the -I option are supported. Again, more effort than it's worth, especially since it looks like reusing pathopen.e is not possible (too many dependencies on other parts of the interpreter).

Search



Quick Links

User menu

Not signed in.

Misc Menu