#!/usr/libexec/platform-python
# createmodule.py - Takes the name of a environment init script and
# produces a modulefile that duplicates the changes made by the init script
# Copyright (C) 2012 by Orion E. Poplawski <orion@cora.nwra.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from optparse import OptionParser
usage = "Usage: %prog [-p prefix] <initscript> [args]"
parser.add_option('-p', '--prefix', action='store', type='string', dest='prefix', help='Specify path prefix')
parser.add_option('--noprefix', action='store_true', dest='noprefix', default=False, help='Do not generate a prefix')
(options, args) = parser.parse_args()
# Determine if running environment is based on cmd.exe or not
return True if platform.system() == 'Windows' else False
# Return environment after a command
# ':' command not supported by cmd.exe
cmd = (cmd if cmd != ':' else '@echo off') + " >nul & set"
cmd = cmd + " >/dev/null;env"
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True)
(stdout, stderr) = p.communicate()
print("ERROR: Could not execute initscript:")
print("%s returned exit code %d" % (cmd, p.returncode))
print("WARNING: initscript sent the following to stderr:")
# Parse the output key=value pairs
for line in stdout.splitlines():
elif iscmdshell() and line.find('=') == -1:
(var,value) = line.split('=',1)
print("ERROR: Could not parse output line:")
# Exported functions - not handled
if value.find('() {') == 0:
#Record initial environment
#Record environment after sourcing the initscript
env2=getenv('"' + args[0] + '" ' + " ".join(args[1:]))
env2=getenv('"' + args[0] + '"')
env2=getenv(". " + " ".join(args))
# Initialize our variables for storing modifications
# Function to nomalize all paths in a list of paths and remove duplicate items
normpath = os.path.normpath(path)
if normpath not in newpaths and normpath != '.':
newpaths.append(os.path.normpath(path))
# Start with existing keys and look for changes
if env1[key] == env2[key]:
#Working directory change
chdir=os.path.normpath(env2[key])
# Determine modifcations to beginning and end of the string
(prepend,append) = env2[key].split(env1[key])
prependpaths = prepend.strip(presep).split(presep)
# LICENSE variables often include paths outside install directory
pathnames += prependpaths
if presep not in prependpath:
newpath = presep.join(normpaths(prependpaths))
prependpath[presep][key] = newpath
unhandled[key] = env2[key]
appendpaths = append.strip(appsep).split(appsep)
# LICENSE variables often include paths outside install directory
if appsep not in appendpath:
newpath = appsep.join(normpaths(appendpaths))
appendpath[appsep][key] = newpath
unhandled[key] = env2[key]
# We're left with new keys in env2
# Use prepend-path for new paths
if (re.search('(DIRS|FILES|PATH)$',key)) or (':' in env2[key]):
prependpaths = env2[key].strip(':').split(':')
# MANPATH can have system defaults added it it wasn't previously set
# LICENSE variables often include paths outside install directory
if key != 'MANPATH' and 'LICENSE' not in key:
pathnames += prependpaths
if ':' not in prependpath:
prependpath[':'][key] = ':'.join(normpaths(prependpaths))
setenv[key] = os.path.normpath(env2[key])
pathnames.append(setenv[key])
for key in unhandled.keys():
print("Unhandled change of", key, file=sys.stderr)
print("Before <%s>" % env1[key], file=sys.stderr)
print("After <%s>" % unhandled[key], file=sys.stderr)
for sepkey in appendpath.keys():
appendpath[sepkey].pop(key, None)
for sepkey in prependpath.keys():
prependpath[sepkey].pop(key, None)
elif not options.noprefix:
prefix = os.path.commonprefix(pathnames).rstrip('/')
# Print out the modulefile
print("\nset prefix " + prefix + "\n")
# Function to format output line with tabs and substituting prefix
def formatline(item, key, value=None):
print("\t"*(2-(len(item)+1)//8), end=' ')
print("\t"*(3-(len(key)+1)//8), end=' ')
# Prefer usage of regular expression to perform a none
# case-sensitive substitution (cygwin vs cmd.exe)
print(re.sub('(?i)' + re.escape(prefix), '$prefix', value))
print(value.replace(prefix,'$prefix'))
# Paths first, grouped by variable name
for sepkey in prependpath.keys():
pathkeys = list(prependpath[sepkey].keys())
formatline("prepend-path",key,prependpath[sepkey][key])
formatline("prepend-path --delim %s" % sepkey,key,prependpath[sepkey][key])
for sepkey in appendpath.keys():
pathkeys = list(appendpath[sepkey].keys())
formatline("append-path",key,appendpath[sepkey][key])
formatline("append-path --delim %s" % sepkey,key,appendpath[sepkey][key])
setenvkeys = list(setenv.keys())
formatline("setenv",key,setenv[key])
formatline("unsetenv",key)