1# This file is part of the LibreOffice project.
2#
3# This Source Code Form is subject to the terms of the Mozilla Public
4# License, v. 2.0. If a copy of the MPL was not distributed with this
5# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7package ClangFormat;
8
9use strict;
10use warnings;
11
12our @EXPORT_OK = qw(get_blacklist set_blacklist get_wanted_version get_own_directory get_extension_regex find check_style);
13
14# Reads the blacklist.
15sub get_blacklist()
16{
17    my $src = "c|cpp|cxx|h|hxx|inl";
18    my %blacklist_names = ();
19
20    # Read the blacklist.
21    if (open(LINES, "solenv/clang-format/blacklist"))
22    {
23        while (my $line = <LINES>)
24        {
25            chomp $line;
26            $blacklist_names{$line} = 1;
27        }
28    }
29
30    return \%blacklist_names;
31}
32
33# Writes the blacklist.
34# The single argument is a reference to an array.
35sub set_blacklist
36{
37    my @filenames = @{$_[0]};
38    open my $fh, ">", "solenv/clang-format/blacklist" or die $!;
39    print $fh "$_\n" for @filenames;
40    close $fh;
41}
42
43# Returns the clang-format version used of style enforcement.
44sub get_wanted_version()
45{
46    return "5.0.0";
47}
48
49# Returns the directory that can host a binary which is used automatically, even
50# if it's not in PATH.
51sub get_own_directory()
52{
53    return "/opt/lo/bin";
54}
55
56# Returns a regex matching filenames we clang-format.
57sub get_extension_regex()
58{
59    return "c|cpp|cxx|h|hxx|inl";
60}
61
62# Use clang-format from CLANG_FORMAT, from our dedicated directory or from
63# PATH, in this order.
64sub find()
65{
66    my $version = get_wanted_version();
67    my $opt_lo = get_own_directory();
68    my $clang_format;
69    if (!(defined($ENV{CLANG_FORMAT}) && is_matching_clang_format_version($ENV{CLANG_FORMAT}, $version)))
70    {
71        my @dirs = split /:/, $ENV{PATH};
72        unshift(@dirs, $opt_lo);
73
74        foreach my $dir (@dirs)
75        {
76            if (is_matching_clang_format_version("$dir/clang-format", $version))
77            {
78                $clang_format = "$dir/clang-format";
79                last;
80            }
81        }
82    }
83    else
84    {
85        $clang_format = $ENV{CLANG_FORMAT};
86    }
87
88    if ($^O eq "cygwin" && defined($clang_format))
89    {
90        $clang_format = `cygpath -m '$clang_format'`;
91        chomp $clang_format;
92    }
93
94    return $clang_format;
95}
96
97# Diffs the original and the formatted version of a single file from the index.
98sub check_style($$)
99{
100    # Make sure that not staged changes are not considered when diffing.
101    my ($clang_format, $filename) = @_;
102    my $index = $filename . ".index";
103    system("git show :$filename > $index");
104    my $format = $index . ".format";
105    system("'$clang_format' -assume-filename=$filename $index > $format");
106    my $ret = system("git --no-pager diff --no-index --exit-code $index $format") == 0;
107    unlink($index);
108    unlink($format);
109    return $ret;
110}
111
112# Private functions.
113
114# Is this binary the version we standardize on?
115sub is_matching_clang_format_version($$)
116{
117    my ($clang_format, $version) = @_;
118    if (! -x $clang_format)
119    {
120        return 0;
121    }
122
123    return `'$clang_format' -version` =~ /^clang-format version $version(-\d+)? \(tags/;
124}
125
1261;
127
128# vim: set shiftwidth=4 softtabstop=4 expandtab:
129