omdev 0.0.0.dev212__py3-none-any.whl → 0.0.0.dev214__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
omdev/.manifests.json CHANGED
@@ -267,7 +267,7 @@
267
267
  "module": ".tools.docker",
268
268
  "attr": "_CLI_MODULE",
269
269
  "file": "omdev/tools/docker.py",
270
- "line": 258,
270
+ "line": 264,
271
271
  "value": {
272
272
  "$.cli.types.CliModule": {
273
273
  "cmd_name": "docker",
omdev/cc/cdeps.py CHANGED
@@ -5,6 +5,7 @@ import typing as ta
5
5
  from omlish import cached
6
6
  from omlish import lang
7
7
  from omlish import marshal as msh
8
+ from omlish.configs import all as configs
8
9
 
9
10
 
10
11
  @dc.dataclass(frozen=True)
@@ -20,19 +21,51 @@ class Cdep:
20
21
 
21
22
  #
22
23
 
24
+ sources: ta.Sequence[str] | None = None
23
25
  include: ta.Sequence[str] | None = None
24
26
 
25
27
  #
26
28
 
27
29
  @dc.dataclass(frozen=True)
28
30
  class Cmake:
29
- fetch_content_url: str | None = None
31
+ @dc.dataclass(frozen=True)
32
+ class FetchContent:
33
+ url: str | None = None
34
+
35
+ @dc.dataclass(frozen=True)
36
+ class Git:
37
+ repository: str | None = None
38
+ tag: str | None = None
39
+
40
+ git: Git | None = None
41
+
42
+ fetch_content: FetchContent | None = None
30
43
 
31
44
  cmake: Cmake | None = None
32
45
 
33
46
 
47
+ def process_marshaled_cdep(obj: ta.Any) -> ta.Any:
48
+ obj = configs.processing.matched_rewrite(
49
+ lambda s: s if isinstance(s, str) else ''.join(s),
50
+ obj,
51
+ ('sources', None),
52
+ ('include', None),
53
+ )
54
+
55
+ return obj
56
+
57
+
34
58
  @cached.function
35
59
  def load_cdeps() -> ta.Mapping[str, Cdep]:
36
60
  src = lang.get_relative_resources(globals=globals())['cdeps.toml'].read_text()
37
61
  dct = tomllib.loads(src)
62
+
63
+ dct = {
64
+ **dct,
65
+ 'deps': {
66
+ k: process_marshaled_cdep(v)
67
+ for k, v in dct.get('deps', {}).items()
68
+ },
69
+ }
70
+
38
71
  return msh.unmarshal(dct.get('deps', {}), ta.Mapping[str, Cdep]) # type: ignore
omdev/cc/cdeps.toml CHANGED
@@ -1,3 +1,18 @@
1
+ [deps.httplib]
2
+ include = ['.']
3
+
4
+ [deps.httplib.git]
5
+ url = 'https://github.com/yhirose/cpp-httplib'
6
+ rev = 'a7bc00e3307fecdb4d67545e93be7b88cfb1e186'
7
+ subtrees = ['httplib.h']
8
+
9
+ [deps.httplib.cmake.fetch_content.git]
10
+ repository = 'https://github.com/yhirose/cpp-httplib.git'
11
+ tag = 'a7bc00e3307fecdb4d67545e93be7b88cfb1e186'
12
+
13
+
14
+ #
15
+
1
16
  [deps.json]
2
17
  include = ['single_include']
3
18
 
@@ -6,8 +21,9 @@ url = 'https://github.com/nlohmann/json'
6
21
  rev = '9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03'
7
22
  subtrees = ['single_include']
8
23
 
9
- [deps.json.cmake]
10
- fetch_content_url = 'https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz'
24
+ [deps.json.cmake.fetch_content]
25
+ url = 'https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz'
26
+
11
27
 
12
28
  #
13
29
 
@@ -19,6 +35,7 @@ url = 'https://github.com/pybind/pybind11'
19
35
  rev = '945e251a6ce7273058b36214d94415e9c9530b8e'
20
36
  subtrees = ['include']
21
37
 
38
+
22
39
  #
23
40
 
24
41
  [deps.ut]
omdev/cc/cli.py CHANGED
@@ -18,6 +18,8 @@ Freestanding options:
18
18
  TODO:
19
19
  - cext interop
20
20
  - gen cmake
21
+ - fix CFLAGS/CCFLAGS/CPPFLAGS/CXXFLAGS
22
+ - jit-gen cmake mode? multi-src builds
21
23
  """
22
24
  import os
23
25
  import shlex
@@ -35,6 +37,7 @@ from .. import magic
35
37
  from ..cache import data as dcache
36
38
  from .cdeps import Cdep
37
39
  from .cdeps import load_cdeps
40
+ from .cdeps import process_marshaled_cdep
38
41
 
39
42
 
40
43
  class Cli(ap.Cli):
@@ -64,16 +67,22 @@ class Cli(ap.Cli):
64
67
  if src_magic.key == '@omlish-cdeps':
65
68
  for dep in check.isinstance(src_magic.prepared, ta.Sequence):
66
69
  if isinstance(dep, ta.Mapping):
67
- dep = msh.unmarshal(dep, Cdep) # type: ignore
70
+ dep = process_marshaled_cdep(dep)
71
+ dep = msh.unmarshal(dep, Cdep)
68
72
  else:
69
73
  dep = load_cdeps()[check.isinstance(dep, str)]
70
74
 
75
+ if dep.sources:
76
+ # TODO
77
+ raise NotImplementedError
78
+
71
79
  dep_spec = dcache.GitSpec(
72
80
  url=dep.git.url,
73
81
  rev=dep.git.rev,
74
82
  subtrees=dep.git.subtrees,
75
83
  )
76
84
  dep_dir = dcache.default().get(dep_spec)
85
+
77
86
  for dep_inc in dep.include or []:
78
87
  inc_dir = os.path.join(dep_dir, dep_inc)
79
88
  check.state(os.path.isdir(inc_dir))
@@ -96,6 +105,9 @@ class Cli(ap.Cli):
96
105
  if cflags := os.environ.get('CFLAGS'):
97
106
  sh_parts.append(cflags) # Explicitly shell-unquoted
98
107
 
108
+ if ldflags := os.environ.get('LDFLAGS'):
109
+ sh_parts.append(ldflags) # Explicitly shell-unquoted
110
+
99
111
  sh_parts.extend([
100
112
  '-std=c++20',
101
113
  shlex.quote(os.path.abspath(src_file)),
omdev/ci/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ # @omlish-lite
omdev/ci/cache.py CHANGED
@@ -1,12 +1,14 @@
1
1
  # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
2
  import abc
4
3
  import os.path
5
- import shlex
6
4
  import shutil
7
5
  import typing as ta
8
6
 
9
- from .shell import ShellCmd
7
+ from omlish.lite.cached import cached_nullary
8
+ from omlish.lite.check import check
9
+ from omlish.lite.logs import log
10
+
11
+ from .consts import CI_CACHE_VERSION
10
12
 
11
13
 
12
14
  ##
@@ -14,12 +16,35 @@ from .shell import ShellCmd
14
16
 
15
17
  @abc.abstractmethod
16
18
  class FileCache(abc.ABC):
19
+ def __init__(
20
+ self,
21
+ *,
22
+ version: int = CI_CACHE_VERSION,
23
+ ) -> None:
24
+ super().__init__()
25
+
26
+ check.isinstance(version, int)
27
+ check.arg(version >= 0)
28
+ self._version = version
29
+
30
+ @property
31
+ def version(self) -> int:
32
+ return self._version
33
+
34
+ #
35
+
17
36
  @abc.abstractmethod
18
- def get_file(self, key: str) -> ta.Optional[str]:
37
+ def get_file(self, key: str) -> ta.Awaitable[ta.Optional[str]]:
19
38
  raise NotImplementedError
20
39
 
21
40
  @abc.abstractmethod
22
- def put_file(self, key: str, file_path: str) -> ta.Optional[str]:
41
+ def put_file(
42
+ self,
43
+ key: str,
44
+ file_path: str,
45
+ *,
46
+ steal: bool = False,
47
+ ) -> ta.Awaitable[str]:
23
48
  raise NotImplementedError
24
49
 
25
50
 
@@ -27,142 +52,96 @@ class FileCache(abc.ABC):
27
52
 
28
53
 
29
54
  class DirectoryFileCache(FileCache):
30
- def __init__(self, dir: str) -> None: # noqa
31
- super().__init__()
32
-
33
- self._dir = dir
34
-
35
- #
36
-
37
- def get_cache_file_path(
55
+ def __init__(
38
56
  self,
39
- key: str,
57
+ dir: str, # noqa
40
58
  *,
41
- make_dirs: bool = False,
42
- ) -> str:
43
- if make_dirs:
44
- os.makedirs(self._dir, exist_ok=True)
45
- return os.path.join(self._dir, key)
59
+ no_create: bool = False,
60
+ no_purge: bool = False,
61
+ **kwargs: ta.Any,
62
+ ) -> None: # noqa
63
+ super().__init__(**kwargs)
46
64
 
47
- def format_incomplete_file(self, f: str) -> str:
48
- return os.path.join(os.path.dirname(f), f'_{os.path.basename(f)}.incomplete')
65
+ self._dir = dir
66
+ self._no_create = no_create
67
+ self._no_purge = no_purge
49
68
 
50
69
  #
51
70
 
52
- def get_file(self, key: str) -> ta.Optional[str]:
53
- cache_file_path = self.get_cache_file_path(key)
54
- if not os.path.exists(cache_file_path):
55
- return None
56
- return cache_file_path
57
-
58
- def put_file(self, key: str, file_path: str) -> None:
59
- cache_file_path = self.get_cache_file_path(key, make_dirs=True)
60
- shutil.copyfile(file_path, cache_file_path)
61
-
62
-
63
- ##
64
-
65
-
66
- class ShellCache(abc.ABC):
67
- @abc.abstractmethod
68
- def get_file_cmd(self, key: str) -> ta.Optional[ShellCmd]:
69
- raise NotImplementedError
70
-
71
- class PutFileCmdContext(abc.ABC):
72
- def __init__(self) -> None:
73
- super().__init__()
74
-
75
- self._state: ta.Literal['open', 'committed', 'aborted'] = 'open'
76
-
77
- @property
78
- def state(self) -> ta.Literal['open', 'committed', 'aborted']:
79
- return self._state
71
+ VERSION_FILE_NAME = '.ci-cache-version'
80
72
 
81
- #
73
+ @cached_nullary
74
+ def setup_dir(self) -> None:
75
+ version_file = os.path.join(self._dir, self.VERSION_FILE_NAME)
82
76
 
83
- @property
84
- @abc.abstractmethod
85
- def cmd(self) -> ShellCmd:
86
- raise NotImplementedError
77
+ if self._no_create:
78
+ check.state(os.path.isdir(self._dir))
87
79
 
88
- #
80
+ elif not os.path.isdir(self._dir):
81
+ os.makedirs(self._dir)
82
+ with open(version_file, 'w') as f:
83
+ f.write(str(self._version))
84
+ return
89
85
 
90
- def __enter__(self):
91
- return self
86
+ with open(version_file) as f:
87
+ dir_version = int(f.read().strip())
92
88
 
93
- def __exit__(self, exc_type, exc_val, exc_tb):
94
- if exc_val is None:
95
- self.commit()
96
- else:
97
- self.abort()
89
+ if dir_version == self._version:
90
+ return
98
91
 
99
- #
92
+ if self._no_purge:
93
+ raise RuntimeError(f'{dir_version=} != {self._version=}')
100
94
 
101
- @abc.abstractmethod
102
- def _commit(self) -> None:
103
- raise NotImplementedError
95
+ dirs = [n for n in sorted(os.listdir(self._dir)) if os.path.isdir(os.path.join(self._dir, n))]
96
+ if dirs:
97
+ raise RuntimeError(
98
+ f'Refusing to remove stale cache dir {self._dir!r} '
99
+ f'due to present directories: {", ".join(dirs)}',
100
+ )
104
101
 
105
- def commit(self) -> None:
106
- if self._state == 'committed':
107
- return
108
- elif self._state == 'open':
109
- self._commit()
110
- self._state = 'committed'
111
- else:
112
- raise RuntimeError(self._state)
102
+ for n in sorted(os.listdir(self._dir)):
103
+ if n.startswith('.'):
104
+ continue
105
+ fp = os.path.join(self._dir, n)
106
+ check.state(os.path.isfile(fp))
107
+ log.debug('Purging stale cache file: %s', fp)
108
+ os.unlink(fp)
113
109
 
114
- #
115
-
116
- @abc.abstractmethod
117
- def _abort(self) -> None:
118
- raise NotImplementedError
119
-
120
- def abort(self) -> None:
121
- if self._state == 'aborted':
122
- return
123
- elif self._state == 'open':
124
- self._abort()
125
- self._state = 'committed'
126
- else:
127
- raise RuntimeError(self._state)
128
-
129
- @abc.abstractmethod
130
- def put_file_cmd(self, key: str) -> PutFileCmdContext:
131
- raise NotImplementedError
110
+ os.unlink(version_file)
132
111
 
112
+ with open(version_file, 'w') as f:
113
+ f.write(str(self._version))
133
114
 
134
- #
115
+ #
135
116
 
117
+ def get_cache_file_path(
118
+ self,
119
+ key: str,
120
+ ) -> str:
121
+ self.setup_dir()
122
+ return os.path.join(self._dir, key)
136
123
 
137
- class DirectoryShellCache(ShellCache):
138
- def __init__(self, dfc: DirectoryFileCache) -> None:
139
- super().__init__()
124
+ def format_incomplete_file(self, f: str) -> str:
125
+ return os.path.join(os.path.dirname(f), f'_{os.path.basename(f)}.incomplete')
140
126
 
141
- self._dfc = dfc
127
+ #
142
128
 
143
- def get_file_cmd(self, key: str) -> ta.Optional[ShellCmd]:
144
- f = self._dfc.get_file(key)
145
- if f is None:
129
+ async def get_file(self, key: str) -> ta.Optional[str]:
130
+ cache_file_path = self.get_cache_file_path(key)
131
+ if not os.path.exists(cache_file_path):
146
132
  return None
147
- return ShellCmd(f'cat {shlex.quote(f)}')
148
-
149
- class _PutFileCmdContext(ShellCache.PutFileCmdContext): # noqa
150
- def __init__(self, tf: str, f: str) -> None:
151
- super().__init__()
152
-
153
- self._tf = tf
154
- self._f = f
155
-
156
- @property
157
- def cmd(self) -> ShellCmd:
158
- return ShellCmd(f'cat > {shlex.quote(self._tf)}')
159
-
160
- def _commit(self) -> None:
161
- os.replace(self._tf, self._f)
162
-
163
- def _abort(self) -> None:
164
- os.unlink(self._tf)
133
+ return cache_file_path
165
134
 
166
- def put_file_cmd(self, key: str) -> ShellCache.PutFileCmdContext:
167
- f = self._dfc.get_cache_file_path(key, make_dirs=True)
168
- return self._PutFileCmdContext(self._dfc.format_incomplete_file(f), f)
135
+ async def put_file(
136
+ self,
137
+ key: str,
138
+ file_path: str,
139
+ *,
140
+ steal: bool = False,
141
+ ) -> str:
142
+ cache_file_path = self.get_cache_file_path(key)
143
+ if steal:
144
+ shutil.move(file_path, cache_file_path)
145
+ else:
146
+ shutil.copyfile(file_path, cache_file_path)
147
+ return cache_file_path