-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathlibraryMngr.py
301 lines (266 loc) · 11.8 KB
/
libraryMngr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# This module manages dog files that form libraries
from inspect import getsourcefile
import os
from os.path import abspath
import re
from pprint import pprint
import codeDogParser
import progSpec
from progSpec import cdlog, cdErr
from pyparsing import ParseResults
libPaths = []
featuresHandled = []
tagsFromLibFiles = {}
currentFilesPath = ""
childLibList = []
'''
T h e b e s t l i b r a r y c h o i c e s f o r y o u r p r o g r a m
And the best programs for your library
Whether you are writing a program for which library choices will be made,
or a library which will be chosen for use with different programs,
you want to have codeDog make the best choices.
You can get the results you want by knowing how CodeDog makes its decisions
about which libraries to use.
The way libraries are chosen in most languages
is that they're given by the programmer.
But code written to work on many different platforms
is easiest to create when the decision is left to the compiler.
In codeDog the problem is solved by telling
the compiler about features and needs instead of about the libraries.
The compiler then chooses which libraries to link from the listed program needs.
There are two kinds of need that the programmer can specify in dog files:
We'll call them 'features' and 'components'. Features are functionality that
must be added to codeDog in order for your program to work (GUI-toolkit,
Unicode, Math-kit, Networking.) Features often correspond to actual libraries.
Components are files that describe libraries that may be specific to particular
platforms. GTK3, XCODE, etc. Both features and components are "needs"
that are specified with tags.
'''
def getTagsFromLibFiles():
"""simple getter for module level variable"""
return tagsFromLibFiles
def collectLibFilenamesFromFolder(folderPath):
for filename in os.listdir(folderPath):
if filename.endswith("Lib.dog"):
libPaths.append(os.path.join(folderPath, filename))
elif filename.endswith("Lib.dog.proxy"):
line = open(os.path.join(folderPath, filename)).readline()
line=line.strip()
baseName = os.path.basename(line)
#print("baseName: {}".format(baseName))
if (filename.strip('.proxy') == baseName):
libPaths.append(os.path.realpath(line))
else:
cdErr("File name "+filename+" does not match path name.")
def collectFromFolderOrLIB(pathName):
collectLibFilenamesFromFolder(pathName)
LIBFOLDER = os.path.join(pathName, "LIBS")
if(os.path.isdir(LIBFOLDER)):
collectLibFilenamesFromFolder(LIBFOLDER)
def findLibraryFiles():
dogFileFolder = os.getcwd()
codeDogFolder = os.path.dirname(os.path.realpath(__file__))
collectFromFolderOrLIB(dogFileFolder)
if(dogFileFolder!=codeDogFolder and dogFileFolder!=codeDogFolder+"/LIBS"):
collectFromFolderOrLIB(codeDogFolder)
def findLibrary(feature):
"""Returns the filepath of LIB that matches '[input].Lib.dog'. If no match
is found returns empty string"""
for libPath in libPaths:
if isinstance(feature, ParseResults) and len(feature)==1:
feature = feature[0]
if os.path.basename(libPath) == feature+".Lib.dog":
return libPath
return ""
def findLibraryChildren(libID):
"""Given a lib prefix string (ie. Logger) return list of paths to children
LIB files (ie. Logger.CPP.Lib.dog, Logger.Android.Lib.dog)"""
libs=[]
for item in libPaths:
itemBaseName = os.path.basename(item)
if(itemBaseName.endswith('Lib.dog') and itemBaseName.startswith(libID)):
innerName = itemBaseName[len(libID)+1:-8]
if (innerName != '' and innerName.find('.')==-1):
libs.append(item)
return libs
def replaceFileName(fileMatch):
global currentFilesPath
fileName = fileMatch.group(1)
currentWD = os.getcwd()
pathName = abspath(currentWD) +"/"+fileName
if not os.path.isfile(pathName):
dirname, filename = os.path.split(abspath(getsourcefile(lambda:0)))
pathName = dirname +"/"+fileName
if not os.path.isfile(pathName):
pathName = currentFilesPath +"/"+fileName
if not os.path.isfile(pathName):
cdErr("Cannot find include file '"+fileName+"'")
includedStr = progSpec.stringFromFile(pathName)
includedStr = processIncludedFiles(includedStr, pathName)
return includedStr
def processIncludedFiles(fileString, fileName):
global currentFilesPath
dirname, filename = os.path.split(abspath(fileName))
currentFilesPath = dirname
pattern = re.compile(r'#include +([\w -\.\/\\]+)')
return pattern.sub(replaceFileName, fileString)
def loadTagsFromFile(fileName):
codeDogStr = progSpec.stringFromFile(fileName)
if codeDogStr == None: return None
codeDogStr = processIncludedFiles(codeDogStr, fileName)
cdlog(2, "Parsing file: "+str(fileName))
return codeDogParser.parseCodeDogLibTags(codeDogStr)
def filterReqTags(ReqTags):
'''Change requirement tags from a list containing one parseResult element
to a list of lists containing strings. (see exceptions). Each inner list
corresponds to an element in the requirements list in the LIB.
Exception: when requirement is in another list, ie. cases of tagOneOf,
the element is appended to inner list as ParseResults rather than string.
TODO: look into extracting the ParseResults
'''
filteredTags=[]
for ReqTag in ReqTags:
filteredTag = []
for tagItem in ReqTag.tagListContents:
filteredTag.append(tagItem.tagValue[0])
filteredTags.append(filteredTag)
return filteredTags
def extractLibTags(library):
libTags = loadTagsFromFile(library)
if libTags == None: cdErr("No library found for feature: " + feature)
tagsFromLibFiles[library] = libTags
ReqTags = progSpec.fetchTagValue([libTags], 'requirements')
if ReqTags == None:
ReqTags =[]
elif len(ReqTags)>0:
ReqTags = filterReqTags(ReqTags)
interfaceTags = progSpec.fetchTagValue([libTags], 'interface')
if interfaceTags == None:
interfaceTags =[]
featuresTags = progSpec.fetchTagValue([libTags], 'featuresNeeded')
featuresNeeded =[]
if featuresTags:
for feature in featuresTags:
featuresNeeded.append(['feature',feature])
return [ReqTags,interfaceTags,featuresNeeded]
def libListType(libList):
if isinstance(libList, str): return "STRING"
op=libList[0]
if (op=='AND' or op=='OR'):
return op
print("WHILE EXAMINING:", libList)
cdErr('Invalid type encountered for a library identifier:'+str(op))
def reduceSolutionOptions(options, indent):
#print indent+"OPTIONS:", options
optionsOp=libListType(options)
if optionsOp != 'STRING':
i=0
while i<len(options[1]):
opt=options[1][i]
if not isinstance(opt, str):
reduceSolutionOptions(options[1][i], indent+'| ')
if len(opt[1])==0:
del options[1][i]
cdlog(1, "DELETED:", i)
continue
changesMade=True
while changesMade:
changesMade=False
opt=options[1][i]
optOp=libListType(opt)
optionsOp=libListType(options)
if (optOp=='AND' or optOp=='OR') and (optOp==optionsOp or len(opt[1])==1): # Both AND or both OR so unwrap child list
options[1][i:i+1]=opt[1]
changesMade=True
#elif optionsOp=='AND' and optOp=='OR':print("CROSS")
# removeDuplicates(options) # TODO: Make this line remove duplicates
i+=1
def fetchFeaturesNeededByLibrary(feature):
libFile = findLibrary(feature)
libTags = loadTagsFromFile(libFile)
if libTags == None: cdErr("No library found for feature: " + feature)
if 'featuresNeeded' in libTags:
featuresNeeded = libTags['featuresNeeded']
return featuresNeeded
return []
def checkIfLibFileMightSatisyNeedWithRequirements(tags, need, libFile, indent):
[ReqTags,interfaceTags,featuresNeeded] = extractLibTags(libFile)
Requirements = []
LibCanWork=True
if need[0] == 'require':
LibCanWork=False
if 'provides' in interfaceTags:
if need[1] in interfaceTags['provides']:
#print(indent, '{}REQUIRE: {} in {}'.format(indent, need[1], interfaceTags['provides']))
LibCanWork = True
for ReqTag in ReqTags:
#print("REQUIREMENT: {}".format(ReqTag))
if ReqTag[0]=='feature':
print("\n Nested Features should be implemented. Please implement them. (", ReqTag[1], ")n")
exit(2)
elif ReqTag[0]=='require':
Requirements.append(ReqTag)
elif ReqTag[0]=='tagOneOf':
tagToCheck = ReqTag[1]
validValues = progSpec.extractListFromTagList(ReqTag[2])
parentTag = progSpec.fetchTagValue(tags, tagToCheck) # E.g.: "platform"
if parentTag==None:
LibCanWork=False
cdErr("ERROR: The tag '"+ tagToCheck + "' was not found in" + libFile + ".\n")
if not parentTag in validValues: LibCanWork=False
return [LibCanWork, Requirements, featuresNeeded]
def constructORListFromFiles(tags, need, files, indent):
global childLibList
OR_List = ['OR', []]
for libFile in files:
#print("{}LIB FILE: {}".format(indent, libFile))
[LibCanWork, Requirements, featuresNeeded] = checkIfLibFileMightSatisyNeedWithRequirements(tags, need, libFile, indent)
if(LibCanWork):
#print("{} LIB CAN WORK: {}".format(indent, libFile))
childFileList = findLibraryChildren(os.path.basename(libFile)[:-8])
if len(childFileList)>0:
childLibList = childLibList + childFileList
solutionOptions = constructANDListFromNeeds(tags, Requirements+featuresNeeded, childFileList, indent + "| ")
solutionOptions[1] = [libFile] + solutionOptions[1]
OR_List[1].append(solutionOptions)
else: OR_List[1].append(libFile)
if len(OR_List[1])==1 and isinstance(OR_List[1][0], str):
return OR_List[1][0] # Optimization
return OR_List
def constructANDListFromNeeds(tags, needs, files, indent):
AND_List = ['AND', []]
for need in needs:
#print(indent, "**need*: ", need)
if need[0] == 'feature':
if need[1] in featuresHandled: continue
cdlog(1, "FEATURE: "+str(need[1]))
featuresHandled.append(need[1])
filesToTry = [findLibrary(need[1])]
if filesToTry[0]=='': cdErr('Could not find a dog file for feature '+need[1])
else:
filesToTry = files
if len(filesToTry)>0:
solutionOptions = constructORListFromFiles(tags, need, filesToTry, indent + "| ")
if len(solutionOptions[1])>0:
AND_List[1].append(solutionOptions)
progSpec.setLibLevels(childLibList)
return AND_List
def ChooseLibs(classes, buildTags, tags):
"""Entry point to libraryMngr
tags: dict
"""
featuresHandled.clear()
cdlog(0, "\n############## C H O O S I N G L I B R A R I E S")
featuresNeeded = progSpec.fetchTagValue([tags], 'featuresNeeded')
initialNeeds1 = []
for feature in featuresNeeded:
featuresNeeded.extend(fetchFeaturesNeededByLibrary(feature))
if not feature in initialNeeds1: initialNeeds1.append(feature)
initialNeeds2 =[]
for feature in initialNeeds1:
initialNeeds2.append(['feature',feature])
solutionOptions = constructANDListFromNeeds([tags, buildTags], initialNeeds2, [], "")
reduceSolutionOptions(solutionOptions, '')
for libPath in solutionOptions[1]:
cdlog(2, "USING LIBRARY:"+str(libPath))
return solutionOptions[1]