fileconfig.lua 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. --
  2. -- fileconfig.lua
  3. -- The set of configuration information for a specific file.
  4. -- Copyright (c) 2011-2014 Jason Perkins and the Premake project
  5. --
  6. local p = premake
  7. p.fileconfig = {}
  8. local fileconfig = p.fileconfig
  9. local context = p.context
  10. local project = p.project
  11. --
  12. -- A little confusing: the file configuration actually contains two objects.
  13. -- The first object, the one that is returned by fileconfig.new() and later
  14. -- passed back in as *the* file configuration object, contains the common
  15. -- project-wide settings for the file. This object also contains a list of
  16. -- "sub-configurations", one for each project configuration to which the file
  17. -- belongs.
  18. --
  19. -- Internally, I'm calling the first object the "file configuration" (fcfg)
  20. -- and the children "file sub-configurations" (fsub). To distinguish them
  21. -- from the project configurations (cfg).
  22. --
  23. -- Define metatables for each of types; more info below.
  24. --
  25. fileconfig.fcfg_mt = {}
  26. fileconfig.fsub_mt = {}
  27. --
  28. -- Create a new file configuration object.
  29. --
  30. -- @param fname
  31. -- The absolute path to the file.
  32. -- @param prj
  33. -- The project which contains the file.
  34. -- @return
  35. -- A new file configuration object.
  36. --
  37. function fileconfig.new(fname, prj)
  38. local environ = { }
  39. local fcfg = context.new(prj, environ)
  40. context.copyFilters(fcfg, prj)
  41. context.addFilter(fcfg, "files", fname:lower())
  42. for key, value in pairs(prj.environ) do
  43. environ[key] = value
  44. end
  45. environ.file = fcfg
  46. context.compile(fcfg)
  47. fcfg.project = prj
  48. fcfg.workspace = prj.workspace
  49. fcfg.configs = {}
  50. fcfg.abspath = fname
  51. context.basedir(fcfg, prj.location)
  52. -- Most of the other path properties are computed on demand
  53. -- from the file's absolute path.
  54. setmetatable(fcfg, fileconfig.fcfg_mt)
  55. -- Except for the virtual path, which is expensive to compute, and
  56. -- can be used across all the sub-configurations
  57. local vpath = project.getvpath(prj, fname)
  58. if vpath ~= fcfg.abspath then
  59. fcfg.vpath = vpath
  60. end
  61. return fcfg
  62. end
  63. --
  64. -- Associate a new project configuration with a file. It is possible for a
  65. -- file to only appear in a subset of a project's configurations.
  66. --
  67. -- @param fcfg
  68. -- The file configuration to which the project configuration should be
  69. -- associated.
  70. -- @param cfg
  71. -- The project configuration to associate.
  72. --
  73. function fileconfig.addconfig(fcfg, cfg)
  74. local prj = cfg.project
  75. local wks = cfg.workspace
  76. -- Create a new context object for this configuration-file pairing.
  77. -- The context has the ability to pull out configuration settings
  78. -- specific to the file.
  79. local environ = {}
  80. local fsub = context.new(prj, environ)
  81. context.copyFilters(fsub, fcfg)
  82. context.mergeFilters(fsub, cfg)
  83. fcfg.configs[cfg] = fsub
  84. -- set up an environment for expanding tokens contained by this file
  85. -- configuration; based on the configuration's environment so that
  86. -- any magic set up there gets maintained
  87. for key, value in pairs(cfg.environ) do
  88. environ[key] = value
  89. end
  90. for key, value in pairs(fcfg.environ) do
  91. environ[key] = value
  92. end
  93. -- finish the setup
  94. context.compile(fsub)
  95. fsub.abspath = fcfg.abspath
  96. fsub.vpath = fcfg.vpath
  97. fsub.config = cfg
  98. fsub.project = prj
  99. fsub.workspace = wks
  100. -- Set the context's base directory to the project's file system
  101. -- location. Any path tokens which are expanded in non-path fields
  102. -- (such as the custom build commands) will be made relative to
  103. -- this path, ensuring a portable generated project.
  104. context.basedir(fsub, prj.location)
  105. return setmetatable(fsub, fileconfig.fsub_mt)
  106. end
  107. --
  108. -- Retrieve the configuration settings for a particular file/project
  109. -- configuration pairing.
  110. --
  111. -- @param fcfg
  112. -- The file configuration to query.
  113. -- @param cfg
  114. -- The project configuration to query.
  115. -- @return
  116. -- The configuration context for the pairing, or nil if this project
  117. -- configuration is not associated with this file.
  118. --
  119. function fileconfig.getconfig(fcfg, cfg)
  120. return fcfg.configs[cfg]
  121. end
  122. --
  123. -- Checks to see if the project or file configuration contains a
  124. -- custom build rule.
  125. --
  126. -- @param cfg
  127. -- A project or file configuration.
  128. -- @return
  129. -- True if the configuration contains settings for a custom
  130. -- build rule.
  131. --
  132. function fileconfig.hasCustomBuildRule(fcfg)
  133. return fcfg and (#fcfg.buildcommands > 0) and (#fcfg.buildoutputs > 0)
  134. end
  135. --
  136. -- Checks to see if the file configuration contains any unique information,
  137. -- or if it is the same as its parent configuration.
  138. --
  139. -- @param fcfg
  140. -- A file configuration.
  141. -- @return
  142. -- True if the file configuration contains values which differ from the
  143. -- parent project configuration, false otherwise.
  144. --
  145. function fileconfig.hasFileSettings(fcfg)
  146. if not fcfg then
  147. return false
  148. end
  149. for key, field in pairs(p.fields) do
  150. if field.scopes[1] == "config" then
  151. local value = fcfg[field.name]
  152. if value then
  153. if type(value) == "table" then
  154. if #value > 0 then
  155. return true
  156. end
  157. else
  158. return true
  159. end
  160. end
  161. end
  162. end
  163. return false
  164. end
  165. --
  166. -- Rather than store pre-computed strings for all of the path variations
  167. -- (abspath, relpath, vpath, name, etc.) for each file (there can be quite
  168. -- a lot of them) I assign a metatable to the file configuration objects
  169. -- that will build these values on the fly.
  170. --
  171. -- I am using these pseudo-properties, rather than explicit functions, to make
  172. -- it easier to fetch them script tokens (i.e. %{file.relpath} with no need
  173. -- for knowledge of the internal Premake APIs.
  174. --
  175. --
  176. -- The indexer for the file configurations. If I have a path building function
  177. -- to fulfill the request, call it. Else fall back to the context's own value lookups.
  178. --
  179. local fcfg_mt = fileconfig.fcfg_mt
  180. fcfg_mt.__index = function(fcfg, key)
  181. if type(fcfg_mt[key]) == "function" then
  182. return fcfg_mt[key](fcfg)
  183. end
  184. return context.__mt.__index(fcfg, key)
  185. end
  186. --
  187. -- The indexer for the file sub-configurations. Check for a path building
  188. -- function first, and then fall back to the context's own value lookups.
  189. -- TODO: Would be great if this didn't require inside knowledge of context.
  190. --
  191. fileconfig.fsub_mt.__index = function(fsub, key)
  192. if type(fcfg_mt[key]) == "function" then
  193. return fcfg_mt[key](fsub)
  194. end
  195. return context.__mt.__index(fsub, key)
  196. end
  197. --
  198. -- And here are the path building functions.
  199. --
  200. function fcfg_mt.basename(fcfg)
  201. return path.getbasename(fcfg.abspath)
  202. end
  203. function fcfg_mt.directory(fcfg)
  204. return path.getdirectory(fcfg.abspath)
  205. end
  206. function fcfg_mt.reldirectory(fcfg)
  207. return path.getdirectory(fcfg.relpath)
  208. end
  209. function fcfg_mt.name(fcfg)
  210. return path.getname(fcfg.abspath)
  211. end
  212. function fcfg_mt.objname(fcfg)
  213. if fcfg.sequence ~= nil and fcfg.sequence > 0 then
  214. return fcfg.basename .. fcfg.sequence
  215. else
  216. return fcfg.basename
  217. end
  218. end
  219. function fcfg_mt.path(fcfg)
  220. return fcfg.relpath
  221. end
  222. function fcfg_mt.relpath(fcfg)
  223. return project.getrelative(fcfg.project, fcfg.abspath)
  224. end
  225. function fcfg_mt.vpath(fcfg)
  226. -- This only gets called if no explicit virtual path was set
  227. return fcfg.relpath
  228. end
  229. function fcfg_mt.extension(fcfg)
  230. return path.getextension(fcfg.abspath)
  231. end