Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix several bugs and support meson 0.55.1 #24

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ cmake-client.log
dist
*.egg-info
__pycache__
.idea
124 changes: 77 additions & 47 deletions mcw/cmake.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from _datetime import datetime
import os
import sys
import pickle
Expand All @@ -6,22 +7,31 @@
import json
import xml.etree.ElementTree as ETree

from .logging import ServerLogHandler
from .logging import ServerLogHandler, logger_fmt, logger_fmt_with_func
from .util import find_executables
from .commandtool import CommandToolWrapper
from .server import (UnixSocketServer, NamedPipeServer)
from .meson import Meson
from typing import NamedTuple, List, Iterable, Union


class cmake_target_t(NamedTuple):
name: str
id: str
type: str
filename: Union[None, str]

class CMakeWrapper:
"""
Class that emulates CMake commands and translates them to the equivalent in Meson.
"""
debug: int
logger: logging.Logger

def __init__(self):
self.version = [3, 10, 0]
self.path = sys.argv[0]
self.debug = False
self.debug = 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change self.debug to False again, then I will merge your changes.

self.command = 'generate'
self.generator = None
self.build_type = None
Expand All @@ -37,13 +47,22 @@ def __init__(self):
else:
self.server = UnixSocketServer(self)
self.tool = CommandToolWrapper(self)
self.logger = None
self._datetime_str = datetime.now().strftime('%Y%m%d-%H%M%S.%f')
if 'MCW_LOG_LEVEL' in os.environ:
self.debug = int(os.environ['MCW_LOG_LEVEL'])
else:
self.debug = 1
if 'MCW_LOG_TO_DIFFERENT_FILES' in os.environ:
self._log_to_different_files = bool(os.environ['MCW_LOG_TO_DIFFERENT_FILES'])
else:
self._log_to_different_files = False
self.init_logging()

def run(self, args):
self.init_logging()

self.log('(args) "%s"' % args)
self.log('(cwd) "%s"' % os.getcwd())
self.logger.info('(args) "%s"' % args)
self.logger.info('(cwd) "%s"' % os.getcwd())

# debug_connect()

Expand All @@ -59,10 +78,10 @@ def run(self, args):
getattr(self, self.command + '_cmd')()
except RuntimeError as e:
print(e.args[0])
self.log(e)
self.logger.error(e.args[0], exc_info=e)
exit(1)
except Exception as e:
self.log(e)
self.logger.error('Uncaught error.', exc_info=e)
raise e
self.save_cache_entries()

Expand Down Expand Up @@ -204,50 +223,59 @@ def init_logging(self, dir=None):
loggers = []
self.logger = logging.getLogger('CMake Wrapper')
loggers.append(self.logger)
self.meson.logger = logging.getLogger('Meson')
loggers.append(self.meson.logger)
self.server.logger = logging.getLogger('Server')
loggers.append(self.server.logger)
loggers.append(self.meson.get_logger())
loggers.append(self.server.get_logger())

# Cleanup if reinitialized
for logger in loggers:
logger.handlers = []

if not dir and self.debug:
if not dir and self.debug > 0:
dir = os.getcwd()

# Setup handlers and formatters
handlers = []
file_handler = None
if dir:
handler = logging.FileHandler(os.path.join(dir, 'meson-cmake-wrapper.log'))
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s: %(message)s')
handler.setFormatter(formatter)
handlers.append(handler)

if self.debug:
handler = logging.StreamHandler(sys.stderr)
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)s: %(message)s')
handler.setFormatter(formatter)
handlers.append(handler)

handler = ServerLogHandler(self.server)
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)s: %(message)s')
handler.setFormatter(formatter)
handlers.append(handler)

for logger in loggers:
logger.setLevel(logging.INFO)
if self._log_to_different_files:
log_file_name = 'meson-cmake-wrapper-{}.log'.format(self._datetime_str)
else:
log_file_name = 'meson-cmake-wrapper.log'
log_path = os.path.join(dir, log_file_name)

file_handler = logging.FileHandler(log_path)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logger_fmt_with_func)
handlers.append(file_handler)
stderr_handler = logging.StreamHandler(sys.stderr)
stderr_handler.setLevel(logging.WARN)
stderr_handler.setFormatter(logger_fmt)
handlers.append(stderr_handler)
server_handler = ServerLogHandler(self.server)
server_handler.setLevel(logging.WARN)
server_handler.setFormatter(logger_fmt)
handlers.append(server_handler)
logger_level = logging.INFO
if self.debug == 0:
for handler in handlers:
logger.addHandler(handler)

def log(self, msg):
if isinstance(msg, Exception):
self.logger.info(msg, exc_info=msg)
else:
self.logger.info(msg)
handler.setLevel(logging.WARN)
logger_level = logging.WARN
if self.debug >= 2:
server_handler.setLevel(logging.INFO)
logger_level = logging.INFO
if self.debug >= 3:
if file_handler:
file_handler.setLevel(logging.DEBUG)
logger_level = logging.DEBUG

for file_handler in handlers:
if self.debug > 1:
file_handler.setFormatter(logger_fmt_with_func)
for logger in loggers:
if logger_level:
logger.setLevel(logger_level)
for file_handler in handlers:
logger.addHandler(file_handler)

def set_generator(self, generator):
if generator == 'Ninja':
Expand All @@ -264,7 +292,7 @@ def set_generator(self, generator):
raise Exception('Generator not supported: ' + generator)
self.generator = generator

self.log('(generator) "%s"' % self.generator)
self.logger.info('(generator) "%s"' % self.generator)

def set_source_dir(self, source_dir):
if not os.path.exists(source_dir):
Expand All @@ -275,7 +303,7 @@ def set_source_dir(self, source_dir):
self.source_dir = os.path.abspath(source_dir)
self.meson.source_dir = self.source_dir

self.log('(source_dir) "%s"' % self.source_dir)
self.logger.info('(source_dir) "%s"' % self.source_dir)

def set_build_dir(self, build_dir):
if not os.path.exists(build_dir):
Expand All @@ -296,7 +324,7 @@ def set_build_dir(self, build_dir):
with open(meson_file, 'w') as file:
file.write('project(\'empty\')')

self.log('(build_dir) "%s"' % self.build_dir)
self.logger.info('(build_dir) "%s"' % self.build_dir)

def set_build_type(self, build_type):
self.build_type = build_type
Expand All @@ -313,7 +341,7 @@ def set_build_type(self, build_type):

self.cache_entries['CMAKE_BUILD_TYPE'] = (build_type, 'STRING')

self.log('(build_type) "%s"' % self.build_type)
self.logger.info('(build_type) "%s"' % self.build_type)

def get_entry(self, entry):
if entry in self.cache_entries:
Expand Down Expand Up @@ -416,6 +444,7 @@ def gen_cmake_cache(self):
file.write('%s:%s=%s\n' % (entry[0], entry[1][1], entry[1][0]))

def gen_cmake_project(self):
self.logger.warning('call gen_cmake_project()')
with open(os.path.join(self.source_dir, 'CMakeLists.txt'), 'w') as file:
file.write('cmake_minimum_required(VERSION %s)\n' % '.'.join(map(str, self.version)))
file.write('project(%s)\n\n' % self.meson.get_project_name())
Expand Down Expand Up @@ -458,7 +487,7 @@ def gen_codeblocks_project(self):
'name': 'all',
'id': 'all',
'type': 'custom',
'filename': ''
'filename': ['']
}

for target in [all_target] + self.meson.get_targets():
Expand All @@ -473,13 +502,14 @@ def gen_codeblocks_project(self):
'static library': '2',
'shared library': '3',
'custom': '4',
'run': '4'
'run': '4',
'shared module': '3' # Code::Blocks does not have shared module type as cmake and meson
}[target['type']]
ETree.SubElement(build_target, 'Option', {'type': ty})

compiler = self.meson.get_compiler(target)
if compiler:
ETree.SubElement(build_target, 'Option', {'compiler': 'gcc'})
ETree.SubElement(build_target, 'Option', {'compiler': compiler})

compiler = ETree.SubElement(build_target, 'Compiler')
for define in self.meson.get_defines(target):
Expand Down
10 changes: 10 additions & 0 deletions mcw/logging.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import logging

logger_fmt_with_func = logging.Formatter(
'%(asctime)s - %(name)s'
'-%(funcName)s()'
': %(message)s'
)

logger_fmt = logging.Formatter(
'%(asctime)s - %(name)s'
': %(message)s'
)

class ServerLogHandler(logging.Handler):
def __init__(self, server):
Expand Down
33 changes: 19 additions & 14 deletions mcw/meson.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import json
import subprocess
import logging

from .ninja import NinjaBackend

Expand Down Expand Up @@ -28,14 +29,12 @@ def __init__(self, path='meson'):
self.c_compile_commands = None
self.c_compile_commands_target = {}
self.c_default_inc_dirs = {}
self.logger = logging.getLogger('Meson')

def log(self, msg):
if isinstance(msg, Exception):
self.logger.info(msg, exc_info=msg)
else:
self.logger.info(msg)
def get_logger(self):
return self.logger

def call(self, args, show=False):
def call(self, args, show=False) -> str:
child = subprocess.Popen([self.path] + args, stdout=subprocess.PIPE)
fulloutput = b''
while True:
Expand Down Expand Up @@ -89,8 +88,10 @@ def get_project_name(self):
def get_targets(self):
if not self.c_targets:
output = self.call(['introspect', '--targets', self.build_dir])
self.log('(targets) "%s"' % output)
self.logger.debug('(targets) "%s"' % output)
self.c_targets = json.loads(output)
target_names = [ i ['name'] for i in self.c_targets]
self.logger.info('(targets) {}'.format(target_names))
return self.c_targets

def get_target_files(self, target):
Expand All @@ -108,9 +109,9 @@ def get_target_files(self, target):
return self.c_target_files[id]

# Handle meson versions before 0.50.0
self.log('(target) "%s"' % id)
self.logger.debug('(target) "%s"' % id)
output = self.call(['introspect', '--target-files', id, self.build_dir])
self.log('(target files) "%s"' % output)
self.logger.debug('(target files) "%s"' % output)

# Workaround https://github.com/mesonbuild/meson/issues/2783
if output == '':
Expand All @@ -122,14 +123,14 @@ def get_target_files(self, target):
def get_buildsystem_files(self):
if not self.c_buildsystem_files:
output = self.call(['introspect', '--buildsystem-files', self.build_dir])
self.log('(buildsystem files) "%s"' % output)
self.logger.debug('(buildsystem files) "%s"' % output)
self.c_buildsystem_files = json.loads(output)
return self.c_buildsystem_files

def get_project_info(self):
if not self.c_project_info:
output = self.call(['introspect', '--projectinfo', self.build_dir])
self.log('(project info) "%s"' % output)
self.logger.info('(project info) "%s"' % output)
self.c_project_info = json.loads(output)
return self.c_project_info

Expand Down Expand Up @@ -227,10 +228,14 @@ def get_output(self, target):
return os.path.join(self.build_dir, self.get_target_filename(target))

def get_target_filename(self, target):
if self.get_version()[1] >= 50:
return target['filename'][0]
filename = target['filename']

if isinstance(filename, list):
if len(filename) > 1:
self.logger.debug('Target {} has more than 1 filename {}'.format(target['name'], filename))
return filename[0]
else:
return target['filename']
return filename

def get_options(self):
meson_options = []
Expand Down
2 changes: 1 addition & 1 deletion mcw/ninja.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ def build(self, target):
def get_target(self, target_name):
target = next((t for t in self.meson.get_targets() if t['name'] == target_name), None)
if target:
return target['filename']
return target['name']

return target_name
Loading