bdistutils.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. # The contents of this file are subject to the BitTorrent Open Source License
  2. # Version 1.1 (the License). You may not copy or use this file, in either
  3. # source code or executable form, except in compliance with the License. You
  4. # may obtain a copy of the License at http://www.bittorrent.com/license/.
  5. #
  6. # Software distributed under the License is distributed on an AS IS basis,
  7. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. # for the specific language governing rights and limitations under the
  9. # License.
  10. # by David Harrison
  11. import sys, os, shutil
  12. from distutils import core
  13. if sys.platform == "win32":
  14. setup = core.setup
  15. # no need to import anything further. None of the remaining
  16. # functionality is available in windows.
  17. else:
  18. import pwd
  19. from distutils.sysconfig import get_python_lib
  20. import distutils.sysconfig
  21. from stat import S_IMODE, S_IRUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
  22. from daemon import getuid_from_username, getgid_from_username
  23. from daemon import getgid_from_groupname
  24. months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
  25. 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
  26. class SetupException(Exception):
  27. pass
  28. def getuid_for_path(path):
  29. return os.stat(path).st_uid
  30. def seteugid_to_login():
  31. """set effective user id and effective group id to the user and group ids
  32. of the user logged into this terminal."""
  33. uid = pwd.getpwnam(os.getlogin())[2] # search /etc/passwd for uid and
  34. gid = pwd.getpwnam(os.getlogin())[3] # gid of user logged into this
  35. # terminal.
  36. os.setegid(gid)
  37. os.seteuid(uid) # Is there a better way? --Dave
  38. def get_svn_change_code():
  39. """Returns the svn repository's date and revision number for the current
  40. working directory. The returned string has the format 'YYYY_MM_DD_revXXXX'
  41. where XXXX is the revision number."""
  42. def to_dict(lines):
  43. # FRAGILE XXX
  44. splitted = [l.split(':') for l in lines]
  45. pairs = [(s[0].strip(), ':'.join(s[1:]).strip()) for s in splitted]
  46. d = dict(pairs)
  47. return d
  48. # returns date and revision number
  49. d = to_dict(os.popen("svn info").readlines())
  50. url = d["URL"]
  51. revision = int(d["Last Changed Rev"])
  52. date = d["Last Changed Date"]
  53. date = date.split(' ')[0] # keep only "YYYY-MM-DD"
  54. date = "_".join(date.split('-')) # replace dash with underscore
  55. date_rev = "%s_rev%.4d" % (date,revision)
  56. return date_rev
  57. def get_cdv_change_code():
  58. # cdv won't run on the dev machines as root. nfs does not allow
  59. # root access to mounted drives. --Dave
  60. if os.getuid() == 0 and getuid_for_path(".") != 0:
  61. seteugid_to_login()
  62. # fragile. XXXX
  63. l = os.popen("cdv history -c 1").readlines()[0].split(" ")
  64. if os.getuid() == 0:
  65. os.seteuid(0)
  66. #os.setegid(oldgid)
  67. l = [x.strip() for x in l if x.strip() != ''] # remove empty strings.
  68. x,code,x,x,x,x,dow,mo,dom,t,y = l
  69. month = "%.2d" % (months.index(mo)+1)
  70. dom = "%.2d" % int(dom) # single digit day of month like 3 becomes 03
  71. t = "_".join(t.split(':')) # convert ':' to underscores in time.
  72. return y+"_"+month+"_"+dom+"_"+t+"_"+code
  73. def get_install_prefix( appname ):
  74. """Generates directory name /opt/appname_YYYY_MM_DD_revXXXX"""
  75. # fragile. XXXX
  76. #change = get_cdv_change_code()
  77. change = get_svn_change_code()
  78. path = os.path.join("/opt", appname+"_"+change)
  79. return os.path.normpath(path)
  80. def get_unique_install_prefix( appname ):
  81. """Generates a directory name /opt/appname_YYYY_MM_DD_revXX or
  82. /opt/appname_YYYY_MM_DD_revXX_vVVV if the prior exists.
  83. VVV is a counter that is incremented with each install of
  84. the distribution with the same svn change code.
  85. Unlike get_install_prefix, this does not assume that cdv exists
  86. on the system, but instead assumes there is a version.txt
  87. file in the distribution root directory containing the cdv change
  88. date and code information. This file is created in the install
  89. directory whenever bdistutils is run with the installdev option."""
  90. vfile = os.path.join(sys.path[0], "version.txt")
  91. if not os.path.exists(vfile):
  92. raise SetupException( "Cannot derive install prefix from cdv change date "
  93. "code, because there is no version.txt file in the "
  94. "root of the distribution tree." )
  95. cfp = open(vfile, 'r')
  96. change_str = cfp.readline().strip()
  97. prefix = os.path.join("/opt", appname+"_"+change_str)
  98. while os.path.exists(prefix):
  99. path, name = os.path.split(prefix)
  100. code_or_cnt = prefix.split("_")[-1]
  101. if code_or_cnt[0] == 'v':
  102. cnt = int(code_or_cnt[1:])
  103. cnt += 1
  104. prefix = "_".join(prefix.split("_")[:-1])
  105. else:
  106. cnt = 1
  107. prefix = "%s_v%03.f" % (prefix, cnt)
  108. return os.path.normpath(prefix)
  109. def setup( **kwargs ):
  110. """site-specific setup.
  111. If sys.argv[1] is not installdev then this behaves
  112. as python's distutils.core.setup.
  113. If sys.argv[1] is installdev then this installs into a
  114. directory like:
  115. /opt/Mitte_2006_10_16_14_39_51_78a5
  116. The date and time is the commit time for this version in the svn repository
  117. and 78a5 is the code for the version in svn.
  118. Also creates a symbolic link like /opt/mitte pointing to
  119. /opt/Mitte_2006_10_16_14_39_51_78a5.
  120. """
  121. name = kwargs['name']
  122. # setup doesn't like kwargs it doesn't know.
  123. destname = kwargs.get('destname', name)
  124. if kwargs.has_key('destname'): del kwargs['destname']
  125. username = kwargs.get('username',None)
  126. if kwargs.has_key('username'): del kwargs['username']
  127. groupname = kwargs.get('groupname',None)
  128. if kwargs.has_key('groupname'): del kwargs['groupname']
  129. symlinks = kwargs.get('symlinks',None)
  130. if kwargs.has_key('symlinks'): del kwargs['symlinks']
  131. installdev=False
  132. installprod = False
  133. old_prefix = None
  134. if len(sys.argv)>1 and sys.argv[1] == "force-installdev":
  135. # force install simply installs in a new directory.
  136. sys.prefix = get_unique_install_prefix(destname)
  137. distutils.sysconfig.PREFIX=sys.prefix
  138. print "get_unique_install_prefix returned sys.prefix=", sys.prefix
  139. installdev = True
  140. sys.argv[1] = "install"
  141. # determine old install directory.
  142. if os.path.exists( os.path.join("/opt/",destname) ):
  143. old_prefix = os.path.realpath(os.path.join("/opt/", destname))
  144. old_prefix = os.path.split(old_prefix)[0]
  145. elif len(sys.argv)>1 and sys.argv[1] == "installdev":
  146. installdev=True
  147. sys.argv[1] = "install"
  148. # create change code file.
  149. code = get_svn_change_code()
  150. if code:
  151. # may fail if root and destination is nfs mounted.
  152. try:
  153. cfp = open(os.path.join(sys.path[0],"version.txt"), 'w')
  154. cfp.write( code )
  155. cfp.close()
  156. except IOError:
  157. # try again as login username.
  158. old_uid = os.geteuid()
  159. seteugid_to_login()
  160. cfp = open(os.path.join(sys.path[0],"version.txt"), 'w')
  161. cfp.write( code )
  162. cfp.close()
  163. os.seteuid(old_uid) # require root access to install into /opt or python site-packages.
  164. # determine install directory
  165. sys.prefix = get_install_prefix(destname)
  166. distutils.sysconfig.PREFIX=sys.prefix
  167. if os.path.exists(sys.prefix):
  168. raise SetupException( "This code revision has already been installed %s."
  169. " If you want to install it again then move the "
  170. "existing directory or use force-installdev." % sys.prefix )
  171. # determine old install directory.
  172. if os.path.exists( os.path.join("/opt/",destname) ):
  173. old_prefix = os.path.realpath(os.path.join("/opt/", destname))
  174. old_prefix = os.path.split(old_prefix)[0]
  175. if len(sys.argv)>1 and sys.argv[1] == "install":
  176. # building with root privilege can fail if the destination of the
  177. # build is nfs mounted.
  178. sys.argv[1] = "build"
  179. try:
  180. # try as root if I am root.
  181. core.setup(**kwargs)
  182. except:
  183. # try using login username
  184. old_uid = os.geteuid()
  185. seteugid_to_login()
  186. core.setup(**kwargs)
  187. os.seteuid(old_uid)
  188. sys.argv[1] = "install"
  189. try:
  190. core.setup(**kwargs)
  191. except:
  192. # try using login username
  193. old_uid = os.geteuid()
  194. seteugid_to_login()
  195. core.setup(**kwargs)
  196. os.seteuid(old_uid)
  197. if installdev:
  198. print "installdev is True."
  199. # shortened the directory path.
  200. #long_path = os.path.join(sys.path[0], "build", "lib", name)
  201. long_path = os.path.join(sys.prefix, "lib", "python2.4", "site-packages", name)
  202. print "long_path=",long_path
  203. dest = os.path.join(sys.prefix,name)
  204. print "dest=", dest
  205. if os.path.exists(long_path):
  206. print "copytree from ", long_path, " to ", dest
  207. shutil.copytree(long_path,dest)
  208. #shutil.rmtree(os.path.join(sys.prefix, "lib" ))
  209. # copy all files not in packages into /opt.
  210. for f in os.listdir('.'):
  211. if f == "build": continue
  212. if f == ".cdv": continue
  213. if f == ".svn": continue
  214. if f == "lib": continue
  215. if not os.path.exists( os.path.join(sys.prefix,f)):
  216. if os.path.isdir(f):
  217. shutil.copytree(f,os.path.join(sys.prefix,f),False)
  218. else:
  219. shutil.copyfile(f,os.path.join(sys.prefix,f))
  220. # create symlink from /opt/blah to /opt/blah_YYYY_MM_DD_HH:MM:SS_code
  221. link_to = sys.prefix
  222. symlnk = os.path.join( '/opt', destname )
  223. print "removing symlink from", symlnk
  224. if os.path.islink(symlnk):
  225. print "removing", symlnk
  226. os.remove(symlnk)
  227. print "creating symlink", symlnk, "to", link_to
  228. os.symlink(link_to, symlnk)
  229. if username:
  230. uid = getuid_from_username(username)
  231. else:
  232. uid = -1
  233. if groupname:
  234. gid = getgid_from_groupname(groupname)
  235. elif username:
  236. gid = getgid_from_username(username)
  237. else:
  238. gid = -1
  239. # recursively change owner and group name of install directory.
  240. ## Turns out that this is a bad idea. The account in which the
  241. ## service runs should not own its own install directory, because
  242. ## it could modify its own code.
  243. #if uid != -1 or gid != -1:
  244. # os.chown(sys.prefix,uid,gid)
  245. # dirs = os.walk(sys.prefix)
  246. # for path, dirnames, filenames in dirs:
  247. # for dir in dirnames:
  248. # os.chown(os.path.join(path, dir),uid,gid)
  249. # for fname in filenames:
  250. # os.chown(os.path.join(path, fname),uid,gid)
  251. # make world readable and make directories world cd'able (i.e., world executable)
  252. dirs = os.walk(sys.prefix)
  253. for path, dirnames, filenames in dirs:
  254. for dir in dirnames:
  255. dir = os.path.join(path,dir)
  256. mode = os.stat(dir).st_mode
  257. mode = S_IMODE(mode)
  258. mode |= S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH
  259. os.chmod(dir,mode)
  260. for fname in filenames:
  261. fname = os.path.join(path, fname)
  262. mode = os.stat(fname).st_mode
  263. mode |= S_IRUSR | S_IRGRP | S_IROTH
  264. os.chmod(fname, mode)
  265. # create pid dir.
  266. pid_dir = os.path.join("/var/run/", name )
  267. if not os.path.exists(pid_dir):
  268. os.mkdir(pid_dir)
  269. os.chown(pid_dir,uid,gid)