xref: /haiku/src/tools/update_package_requires/update_package_requires.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <list>
13 #include <map>
14 
15 #include <package/PackageInfo.h>
16 #include <package/PackageResolvable.h>
17 #include <package/PackageResolvableExpression.h>
18 #include <package/RepositoryCache.h>
19 
20 
21 using namespace BPackageKit;
22 
23 
24 typedef std::list<BPackageResolvable*> ProvidesList;
25 
26 
27 static const char* sProgramName = "update_package_requires";
28 
29 
30 #define DIE(result, msg...)											\
31 do {																\
32 	fprintf(stderr, "*** " msg);									\
33 	fprintf(stderr, " : %s\n", strerror(result));					\
34 	exit(5);														\
35 } while(0)
36 
37 
38 static void
39 print_usage_and_exit(bool error)
40 {
41 	fprintf(error ? stderr : stdout,
42 		"Usage: %s <package info> <repository>\n"
43 		"Updates the versions in the \"requires\" list of the given package\n"
44 		"info file using the available package information from the given\n"
45 		"repository cache file <repository>.\n",
46 		sProgramName);
47 	exit(error ? 1 : 0);
48 }
49 
50 
51 static void
52 update_requires_expression(BPackageResolvableExpression& expression,
53 	const ProvidesList& providesList)
54 {
55 	// find the best-matching provides
56 	BPackageResolvable* bestProvides = NULL;
57 	for (ProvidesList::const_iterator it = providesList.begin();
58 		it != providesList.end(); ++it) {
59 		BPackageResolvable* provides = *it;
60 		if (!expression.Matches(*provides))
61 			continue;
62 
63 		if (bestProvides == NULL || bestProvides->Version().InitCheck() != B_OK
64 			|| (provides->Version().InitCheck() == B_OK
65 				&& provides->Version().Compare(bestProvides->Version()) > 0)) {
66 			bestProvides = provides;
67 		}
68 	}
69 
70 	if (bestProvides == NULL || bestProvides->Version().InitCheck() != B_OK)
71 		return;
72 
73 	// Update the expression. Enforce the minimum found version, if the requires
74 	// has no version requirement or also a minimum. Otherwise enforce the exact
75 	// version found.
76 	BPackageResolvableOperator newOperator = B_PACKAGE_RESOLVABLE_OP_EQUAL;
77 	switch (expression.Operator()) {
78 		case B_PACKAGE_RESOLVABLE_OP_LESS:
79 		case B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL:
80 		case B_PACKAGE_RESOLVABLE_OP_EQUAL:
81 		case B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL:
82 			break;
83 		case B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL:
84 		case B_PACKAGE_RESOLVABLE_OP_GREATER:
85 		case B_PACKAGE_RESOLVABLE_OP_ENUM_COUNT:
86 			newOperator = B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL;
87 			break;
88 	}
89 
90 	expression.SetTo(expression.Name(), newOperator, bestProvides->Version());
91 }
92 
93 
94 int
95 main(int argc, const char* const* argv)
96 {
97 	if (argc != 3)
98 		print_usage_and_exit(true);
99 
100 	if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)
101 		print_usage_and_exit(false);
102 
103 	const char* const packageInfoPath = argv[1];
104 	const char* const repositoryCachePath = argv[2];
105 
106 	// read the repository cache
107 	BRepositoryCache repositoryCache;
108 	status_t error = repositoryCache.SetTo(repositoryCachePath);
109 	if (error != B_OK) {
110 		DIE(error, "failed to read repository cache file \"%s\"",
111 			repositoryCachePath);
112 	}
113 
114 	// create a map for all provides (name -> resolvable list)
115 	typedef std::map<BString, ProvidesList> ProvidesMap;
116 
117 	ProvidesMap providesMap;
118 
119 	for (BRepositoryCache::Iterator it = repositoryCache.GetIterator();
120 		const BPackageInfo* info = it.Next();) {
121 		const BObjectList<BPackageResolvable>& provides = info->ProvidesList();
122 		int32 count = provides.CountItems();
123 		for (int32 i = 0; i < count; i++) {
124 			BPackageResolvable* resolvable = provides.ItemAt(i);
125 			ProvidesList& providesList = providesMap[resolvable->Name()];
126 			providesList.push_back(resolvable);
127 		}
128 	}
129 
130 	// load the package info
131 	BPackageInfo packageInfo;
132 	error = packageInfo.ReadFromConfigFile(packageInfoPath);
133 	if (error != B_OK)
134 		DIE(error, "failed to read package info file \"%s\"", packageInfoPath);
135 
136 	// clone the package info's requires list
137 	typedef std::list<BPackageResolvableExpression> RequiresList;
138 	RequiresList requiresList;
139 	int32 requiresCount = packageInfo.RequiresList().CountItems();
140 	for (int32 i = 0; i < requiresCount; i++)
141 		requiresList.push_back(*packageInfo.RequiresList().ItemAt(i));
142 
143 	// rebuild the requires list with updated versions
144 	packageInfo.ClearRequiresList();
145 	for (RequiresList::iterator it = requiresList.begin();
146 		it != requiresList.end(); ++it) {
147 		BPackageResolvableExpression expression = *it;
148 		ProvidesMap::iterator foundIt = providesMap.find(expression.Name());
149 		if (foundIt != providesMap.end())
150 			update_requires_expression(expression, foundIt->second);
151 
152 		error = packageInfo.AddRequires(expression);
153 		if (error != B_OK)
154 			DIE(error, "failed to add requires item to package info");
155 	}
156 
157 	// write updated package info
158 	BString configString;
159 	error = packageInfo.GetConfigString(configString);
160 	if (error != B_OK)
161 		DIE(error, "failed to get updated package info string");
162 
163 	FILE* file = fopen(packageInfoPath, "w");
164 	if (file == NULL) {
165 		DIE(errno, "failed to open package info file \"%s\" for writing",
166 			packageInfoPath);
167 	}
168 
169 	if (fwrite(configString.String(), configString.Length(), 1, file) != 1) {
170 		DIE(errno, "failed to write updated package info file \"%s\"",
171 			packageInfoPath);
172 	}
173 
174 	fclose(file);
175 
176 	return 0;
177 }
178