Hello, attached is my implementation of basic build support for using Clang compiler plugin during building LO. I'm posting it here first in case our build system people have different ideas to how I have integrated it in the build system, if there are no comments, I will push it.
-- Lubos Lunak l.lu...@suse.cz
From 6c63f9809ecaa63e2db6fb606b573ab12f09752f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= <l.lu...@suse.cz> Date: Fri, 5 Oct 2012 18:17:13 +0200 Subject: [PATCH] initial support for clang compiler plugins The plugin is intentionally built using a custom Makefile, because it's used by gbuild, so I don't want to build the plugin using gbuild too. It is also intentionally not placed under workdir/, as that is cleaned by 'make clean', the plugin is cleaned only by 'make distclean', so that cleaning it doesn't cause ccache misses. Right now there's a relatively trivial plugin that just checks for unused rtl::OUString variables. Change-Id: Ic05eba8d6260eec123c9e699eb5385abfe1b832f --- Makefile.top | 6 ++- compilerplugins/.gitignore | 1 + compilerplugins/Makefile | 23 +++++++++ compilerplugins/Makefile-clang.mk | 47 +++++++++++++++++ compilerplugins/Makefile.mk | 29 +++++++++++ compilerplugins/clang/compileplugin.cxx | 66 ++++++++++++++++++++++++ compilerplugins/clang/compileplugin.hxx | 14 ++++++ compilerplugins/clang/unusedvariablecheck.cxx | 67 +++++++++++++++++++++++++ compilerplugins/clang/unusedvariablecheck.hxx | 36 +++++++++++++ config_host.mk.in | 1 + configure.in | 39 ++++++++++++++ solenv/gbuild/platform/com_GCC_class.mk | 2 + solenv/gbuild/platform/com_GCC_defs.mk | 6 +++ 13 files changed, 335 insertions(+), 2 deletions(-) create mode 100644 compilerplugins/.gitignore create mode 100644 compilerplugins/Makefile create mode 100644 compilerplugins/Makefile-clang.mk create mode 100644 compilerplugins/Makefile.mk create mode 100644 compilerplugins/clang/compileplugin.cxx create mode 100644 compilerplugins/clang/compileplugin.hxx create mode 100644 compilerplugins/clang/unusedvariablecheck.cxx create mode 100644 compilerplugins/clang/unusedvariablecheck.hxx diff --git a/Makefile.top b/Makefile.top index bb632a2..bd85703 100644 --- a/Makefile.top +++ b/Makefile.top @@ -350,10 +350,12 @@ ifeq ($(CROSS_COMPILING),YES) rm -rf $(SRCDIR)/*/$(INPATH_FOR_BUILD) endif +include $(SRCDIR)/compilerplugins/Makefile.mk + # # Distclean # -distclean : clean +distclean : clean compilerplugins-clean ifeq ($(BUILD_DMAKE),YES) (if [ -f dmake/Makefile ] ; then $(GNUMAKE) -j $(GMAKE_PARALLELISM) -C dmake distclean; fi) && \ rm -f solenv/*/bin/dmake* @@ -391,7 +393,7 @@ endif # bootstrap: $(WORKDIR)/bootstrap -$(WORKDIR)/bootstrap: solenv/bin/concat-deps.c +$(WORKDIR)/bootstrap: solenv/bin/concat-deps.c compilerplugins @cd $(SRCDIR) && ./bootstrap @mkdir -p $(dir $@) && touch $@ diff --git a/compilerplugins/.gitignore b/compilerplugins/.gitignore new file mode 100644 index 0000000..b672fde --- /dev/null +++ b/compilerplugins/.gitignore @@ -0,0 +1 @@ +obj diff --git a/compilerplugins/Makefile b/compilerplugins/Makefile new file mode 100644 index 0000000..4281c12 --- /dev/null +++ b/compilerplugins/Makefile @@ -0,0 +1,23 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +ifeq ($(SOLARENV),) +ifeq ($(gb_Side),) +gb_Side:=host +endif +include $(dir $(realpath $(lastword $(MAKEFILE_LIST))))../config_$(gb_Side).mk +endif + +include $(SRCDIR)/compilerplugins/Makefile.mk + +all: build +build: compilerplugins +clean: compilerplugins-clean + +# vim: set noet sw=4 ts=4: diff --git a/compilerplugins/Makefile-clang.mk b/compilerplugins/Makefile-clang.mk new file mode 100644 index 0000000..9c93931 --- /dev/null +++ b/compilerplugins/Makefile-clang.mk @@ -0,0 +1,47 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Make sure variables in this Makefile do not conflict with other variables (e.g. from gbuild). + +CLANGSRC=compileplugin.cxx unusedvariablecheck.cxx + +CLANGDIR=/usr +CLANGBUILD=/usr +CLANGDEFS=-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fno-rtti +CLANGINCLUDES=-I$(CLANGDIR)/include -I$(CLANGDIR)/tools/clang/include -I$(CLANGBUILD)/include -I$(CLANGBUILD)/tools/clang/include +CLANGCXXFLAGS=-O2 -Wall -g + +CLANGINDIR=$(SRCDIR)/compilerplugins/clang +# Cannot use $(WORKDIR), the plugin should survive even 'make clean', otherwise the rebuilt +# plugin will cause cache misses with ccache. +CLANGOUTDIR=$(SRCDIR)/compilerplugins/obj + +compilerplugins: $(CLANGOUTDIR) $(CLANGOUTDIR)/compileplugin.so + +compilerplugins-clean: + rm -rf $(CLANGOUTDIR) + +$(CLANGOUTDIR): + mkdir -p $(CLANGOUTDIR) + +CLANGOBJS= + +define clangbuildsrc +$(2): $(1) $(SRCDIR)/compilerplugins/Makefile-clang.mk + $(CXX) $(CLANGCXXFLAGS) $(CLANGDEFS) $(CLANGINCLUDES) $(1) -fPIC -c -o $(2) + +$(CLANGOUTDIR)/compileplugin.so: $(2) +$(CLANGOUTDIR)/compileplugin.so: CLANGOBJS += $(2) +endef + +$(foreach src, $(CLANGSRC), $(eval $(call clangbuildsrc, $(CLANGINDIR)/$(src), $(CLANGOUTDIR)/$(src:.cxx=.o)))) + +$(CLANGOUTDIR)/compileplugin.so: $(CLANGOBJS) + $(CXX) -shared $(CLANGOBJS) -o $@ + +# vim: set noet sw=4 ts=4: diff --git a/compilerplugins/Makefile.mk b/compilerplugins/Makefile.mk new file mode 100644 index 0000000..91a5daa --- /dev/null +++ b/compilerplugins/Makefile.mk @@ -0,0 +1,29 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +.PHONY: compilerplugins compilerplugins-clean + +ifeq ($(COMPILER_PLUGINS),) + +# no support + +compilerplugins: +compilerplugins-clean: + +else + +ifeq ($(COM_GCC_IS_CLANG),TRUE) + +include $(SRCDIR)/compilerplugins/Makefile-clang.mk + +endif + +endif + +# vim: set noet sw=4 ts=4: diff --git a/compilerplugins/clang/compileplugin.cxx b/compilerplugins/clang/compileplugin.cxx new file mode 100644 index 0000000..3b3a0cd --- /dev/null +++ b/compilerplugins/clang/compileplugin.cxx @@ -0,0 +1,66 @@ +/* + * This file is part of the LibreOffice project. + * + * Based on LLVM/Clang. + * + * This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + * + */ + +#include "compileplugin.hxx" + +#include <clang/AST/ASTConsumer.h> +#include <clang/Frontend/CompilerInstance.h> +#include <clang/Frontend/FrontendAction.h> +#include <clang/Frontend/FrontendPluginRegistry.h> +#include <clang/Rewrite/Rewriter.h> + +#include "unusedvariablecheck.hxx" + +using namespace clang; + +namespace loplugin +{ + +class AstConsumer + : public ASTConsumer + { + public: + explicit AstConsumer( ASTContext& context ) + : rewriter( context.getSourceManager(), context.getLangOpts()) + , unusedVariableCheck( context ) + { + } + virtual void HandleTranslationUnit( ASTContext& context ) + { + if( context.getDiagnostics().hasErrorOccurred()) + return; + unusedVariableCheck.run(); + // TODO also LO header files? or a subdir? + if( const RewriteBuffer* buf = rewriter.getRewriteBufferFor( context.getSourceManager().getMainFileID())) + buf->write( llvm::outs()); + // TODO else write out the original file? + } + private: + Rewriter rewriter; + UnusedVariableCheck unusedVariableCheck; + }; + +class LibreOfficeAction + : public PluginASTAction + { + public: + virtual ASTConsumer* CreateASTConsumer( CompilerInstance& Compiler, StringRef InFile ) + { + return new AstConsumer( Compiler.getASTContext()); + } + virtual bool ParseArgs( const CompilerInstance& CI, const std::vector< std::string >& args ) + { + return true; + } + }; + +} // namespace + +static FrontendPluginRegistry::Add< loplugin::LibreOfficeAction > X( "loplugin", "LibreOffice compile check plugin" ); diff --git a/compilerplugins/clang/compileplugin.hxx b/compilerplugins/clang/compileplugin.hxx new file mode 100644 index 0000000..30fe24b --- /dev/null +++ b/compilerplugins/clang/compileplugin.hxx @@ -0,0 +1,14 @@ +/* + * This file is part of the LibreOffice project. + * + * Based on LLVM/Clang. + * + * This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + * + */ + +#ifndef COMPILEPLUGIN_H +#define COMPILEPLUGIN_H + +#endif // LOPLUGIN_H diff --git a/compilerplugins/clang/unusedvariablecheck.cxx b/compilerplugins/clang/unusedvariablecheck.cxx new file mode 100644 index 0000000..960eb44 --- /dev/null +++ b/compilerplugins/clang/unusedvariablecheck.cxx @@ -0,0 +1,67 @@ +/* + * This file is part of the LibreOffice project. + * + * Based on LLVM/Clang. + * + * This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + * + */ + +#include "unusedvariablecheck.hxx" + +#include <clang/Basic/SourceManager.h> + +using namespace clang; + +namespace loplugin +{ + +UnusedVariableCheck::UnusedVariableCheck( ASTContext& context ) + : context( context ) + { + } + +void UnusedVariableCheck::run() + { + TraverseDecl( context.getTranslationUnitDecl()); + } + +bool UnusedVariableCheck::VisitNamedDecl( NamedDecl* declaration ) + { + // TODO also LO header files? or a subdir? + if( !context.getSourceManager().isFromMainFile( declaration->getLocStart())) + return true; + if( !isa< VarDecl >( declaration )) + return true; + const VarDecl* var = cast< VarDecl >( declaration ); + if( var->isReferenced() || var->isUsed()) + return true; + if( CXXRecordDecl* type = var->getType()->getAsCXXRecordDecl()) + if( type->getQualifiedNameAsString() == "rtl::OUString" ) + { + DiagnosticsEngine& diag = context.getDiagnostics(); + // TODO WaE ? + unsigned diagid; + if( const ParmVarDecl* param = dyn_cast< ParmVarDecl >( var )) + { + // If this declaration does not have a body, then the parameter is indeed not used, + // so ignore. + if( const FunctionDecl* func = dyn_cast< FunctionDecl >( param->getParentFunctionOrMethod())) + if( !func->doesThisDeclarationHaveABody()) + return true; + diagid = diag.getCustomDiagID( DiagnosticsEngine::Warning, + "unused parameter %0 [loplugin]" ); + diag.Report( param->getLocStart(), diagid ) << param->getDeclName(); + } + else + { + diagid = diag.getCustomDiagID( DiagnosticsEngine::Warning, + "unused variable %0 [loplugin]" ); + diag.Report( var->getLocStart(), diagid ) << var->getDeclName(); + } + } + return true; + } + +} // namespace diff --git a/compilerplugins/clang/unusedvariablecheck.hxx b/compilerplugins/clang/unusedvariablecheck.hxx new file mode 100644 index 0000000..81b3acc --- /dev/null +++ b/compilerplugins/clang/unusedvariablecheck.hxx @@ -0,0 +1,36 @@ +/* + * This file is part of the LibreOffice project. + * + * Based on LLVM/Clang. + * + * This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + * + */ + +#ifndef UNUSEDVARIABLECHECK_H +#define UNUSEDVARIABLECHECK_H + +#include <clang/AST/RecursiveASTVisitor.h> + +using namespace clang; + +namespace loplugin +{ + +typedef std::vector< const Stmt* > StmtParents; + +class UnusedVariableCheck + : public RecursiveASTVisitor< UnusedVariableCheck > + { + public: + explicit UnusedVariableCheck( ASTContext& context ); + void run(); + bool VisitNamedDecl( NamedDecl* declaration ); + private: + ASTContext& context; + }; + +} // namespace + +#endif // UNUSEDVARIABLECHECK_H diff --git a/config_host.mk.in b/config_host.mk.in index 48710b1..66cef50 100644 --- a/config_host.mk.in +++ b/config_host.mk.in @@ -71,6 +71,7 @@ export COMMONS_HTTPCLIENT_JAR=@COMMONS_HTTPCLIENT_JAR@ export COMMONS_LANG_JAR=@COMMONS_LANG_JAR@ export COMMONS_LOGGING_JAR=@COMMONS_LOGGING_JAR@ export COMPATH=@COMPATH@ +export COMPILER_PLUGINS=@COMPILER_PLUGINS@ export COMP_ENV=@OUTPATH@ export COM_FOR_BUILD=@COM_FOR_BUILD@ export CPPUNIT_CFLAGS=@CPPUNIT_CFLAGS@ diff --git a/configure.in b/configure.in index 01960af..00d50bd 100644 --- a/configure.in +++ b/configure.in @@ -789,6 +789,11 @@ AC_ARG_ENABLE(dbgutil, It is not possible to mix object files or libraries from a --enable-dbgutil and a --disable-dbgutil build.])) +AC_ARG_ENABLE(compiler-plugins, + AS_HELP_STRING([--enable-compiler-plugins], + [Enable compiler plugins that will perform additional checks during + building. Enabled automatically by --enable-dbgutil.])) + AC_ARG_ENABLE(linkoo, AS_HELP_STRING([--disable-linkoo], [Disable linkoo for the smoketest installation.])) @@ -4951,6 +4956,40 @@ fi AC_SUBST([VALGRIND_CFLAGS]) dnl =================================================================== +dnl Compiler plugins +dnl =================================================================== + +COMPILER_PLUGINS= +# currently only Clang +if test "$COM_GCC_IS_CLANG" = "TRUE"; then + if test -n "$enable_compiler_plugins"; then + compiler_plugins="$enable_compiler_plugins" + elif test -n "$enable_dbgutil" -a "$enable_dbgutil" != "no"; then + compiler_plugins=test + else + compiler_plugins=no + fi + if test "$compiler_plugins" != "no"; then + AC_LANG_PUSH([C++]) + save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$CPPFLAGS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS" + AC_CHECK_HEADER(clang/AST/RecursiveASTVisitor.h, + [COMPILER_PLUGINS=TRUE], + [ + if test "$compiler_plugins" = "yes"; then + AC_MSG_ERROR([Cannot find Clang headers to build compiler plugins.]) + else + AC_MSG_WARN([Cannot find Clang headers to build compiler plugins, plugins disabled]) + add_warning "Cannot find Clang headers to build compiler plugins, plugins disabled." + fi + ]) + CPPFLAGS=$save_CPPFLAGS + AC_LANG_POP([C++]) + fi +fi +AC_SUBST(COMPILER_PLUGINS) + +dnl =================================================================== dnl Set the MinGW sys-root dnl =================================================================== if test "$WITH_MINGW" = "yes"; then diff --git a/solenv/gbuild/platform/com_GCC_class.mk b/solenv/gbuild/platform/com_GCC_class.mk index 630eed7..aa00267 100644 --- a/solenv/gbuild/platform/com_GCC_class.mk +++ b/solenv/gbuild/platform/com_GCC_class.mk @@ -60,6 +60,7 @@ $(call gb_Helper_abbreviate_dirs,\ $(if $(filter Library,$(TARGETTYPE)),$(gb_Library_LTOFLAGS)) \ $(if $(VISIBILITY),,$(gb_VISIBILITY_FLAGS)) \ $(if $(WARNINGS_NOT_ERRORS),,$(gb_CFLAGS_WERROR)) \ + $(if $(COMPILER_PLUGINS),$(gb_COMPILER_PLUGINS)) \ $(T_CFLAGS) \ -c $(3) \ -o $(1) \ @@ -82,6 +83,7 @@ $(call gb_Helper_abbreviate_dirs,\ $(if $(filter Library,$(TARGETTYPE)),$(gb_Library_LTOFLAGS)) \ $(if $(VISIBILITY),,$(gb_VISIBILITY_FLAGS)) \ $(if $(WARNINGS_NOT_ERRORS),,$(gb_CXXFLAGS_WERROR)) \ + $(if $(COMPILER_PLUGINS),$(gb_COMPILER_PLUGINS)) \ $(T_CXXFLAGS) \ -c $(3) \ -o $(1) \ diff --git a/solenv/gbuild/platform/com_GCC_defs.mk b/solenv/gbuild/platform/com_GCC_defs.mk index 0cc86e3..3d73d5c 100644 --- a/solenv/gbuild/platform/com_GCC_defs.mk +++ b/solenv/gbuild/platform/com_GCC_defs.mk @@ -149,6 +149,12 @@ gb_DEBUG_CXXFLAGS := $(FNO_DEFAULT_INLINE) gb_LinkTarget_INCLUDE := $(filter-out %/stl, $(subst -I. , ,$(SOLARINC))) gb_LinkTarget_INCLUDE_STL := $(filter %/stl, $(subst -I. , ,$(SOLARINC))) +ifeq ($(COM_GCC_IS_CLANG),TRUE) +gb_COMPILER_PLUGINS :=-Xclang -load -Xclang $(SRCDIR)/compilerplugins/obj/compileplugin.so -Xclang -add-plugin -Xclang loplugin +else +gb_COMPILER_PLUGINS := +endif + # Executable class gb_Executable_EXT_for_build := -- 1.7.10.4
_______________________________________________ LibreOffice mailing list LibreOffice@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice