├── fish-12.png ├── .gitignore ├── CMakeLists.txt ├── std.h ├── README.md ├── std.cpp ├── SchaeferMLS.cpp ├── SchaeferMLS.h ├── CurveCSS.h ├── CurveSignature.h ├── CurveCSS.cpp ├── CurveSignature.cpp └── CMake └── cotire.cmake /fish-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/royshil/CurveDeformationMLS/master/fish-12.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (ShapeDeformation) 3 | 4 | set (CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake") 5 | 6 | include(cotire) 7 | 8 | find_package(OpenCV 2.4 REQUIRED) 9 | 10 | add_executable(ShapeDeformation 11 | std.cpp std.h 12 | SchaeferMLS.h SchaeferMLS.cpp 13 | CurveCSS.cpp CurveCSS.h 14 | CurveSignature.cpp CurveSignature.h 15 | ) 16 | 17 | target_link_libraries(ShapeDeformation 18 | ${OpenCV_LIBS} 19 | mgl 20 | ) 21 | 22 | set_target_properties(ShapeDeformation PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "std.h") 23 | set_target_properties(ShapeDeformation PROPERTIES COMPILE_FLAGS "-DWITHOUT_OPENCL") 24 | 25 | cotire(ShapeDeformation) 26 | -------------------------------------------------------------------------------- /std.h: -------------------------------------------------------------------------------- 1 | /* 2 | * std.h 3 | * CurveMatching 4 | * 5 | * Created by Roy Shilkrot on 11/28/12. 6 | * Copyright 2012 MIT. All rights reserved. 7 | * 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace std; 20 | 21 | #include 22 | #include 23 | #include 24 | //#include 25 | #include 26 | 27 | #include 28 | 29 | bool hasEnding (std::string const &fullString, std::string const &ending); 30 | bool hasEndingLower (string const &fullString_, string const &_ending); 31 | void open_imgs_dir(const char* dir_name, std::vector& images_names); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CurveDeformationMLS 2 | =================== 3 | 4 | 2D Shape/Curve deformation using Moving Least Sqaures 5 | 6 | http://www.morethantechnical.com/2013/01/05/shape-manipulation-with-moving-least-squares-for-curves-w-code/ 7 | 8 | Build 9 | ----- 10 | mkdir build 11 | cd build 12 | cmake .. 13 | make 14 | 15 | API 16 | --- 17 | //Read curve 18 | vector a; 19 | GetCurveForImage(imread("a_slihouette.png", a, false); 20 | 21 | //Convert to Point_ - optional 22 | vector a_p2d, a_p2d_smoothed; 23 | ConvertCurve(a, a_p2d); 24 | 25 | //Get curvature extrema points 26 | vector > stringrep = CurvatureExtrema(a_p2d, a_p2d_smoothed,0.05,4.0); 27 | 28 | //Get extrema as control points 29 | vector control_pts; 30 | for(int i=0;i= ending.length()) { 14 | return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); 15 | } else { 16 | return false; 17 | } 18 | } 19 | 20 | bool hasEndingLower (const string &fullString_, const string &_ending) 21 | { 22 | string fullstring = fullString_, ending = _ending; 23 | transform(fullString_.begin(),fullString_.end(),fullstring.begin(),::tolower); // to lower 24 | return hasEnding(fullstring,ending); 25 | } 26 | 27 | 28 | void open_imgs_dir(const char* dir_name, std::vector& images_names) { 29 | if (dir_name == NULL) { 30 | return; 31 | } 32 | 33 | string dir_name_ = string(dir_name); 34 | vector files_; 35 | 36 | DIR *dp; 37 | struct dirent *ep; 38 | dp = opendir (dir_name); 39 | 40 | if (dp != NULL) 41 | { 42 | while (ep = readdir (dp)) { 43 | if (ep->d_name[0] != '.') 44 | files_.push_back(ep->d_name); 45 | } 46 | 47 | (void) closedir (dp); 48 | } 49 | else { 50 | cerr << ("Couldn't open the directory"); 51 | return; 52 | } 53 | for (unsigned int i=0; i smls; 17 | Mat visualized_curve; 18 | vector target_curve; 19 | int mls_def_type; 20 | 21 | void MLSUpdate() { 22 | static int framenum = 0; 23 | 24 | if (mls_def_type == 0) { 25 | smls.UpdateAffine(); 26 | } else if (mls_def_type == 1) { 27 | smls.UpdateSimilarity(); 28 | } else { 29 | smls.UpdateRigid(); 30 | } 31 | 32 | visualized_curve.setTo(0); 33 | smls.Draw(visualized_curve); 34 | imshow("MLS", visualized_curve); 35 | 36 | // stringstream ss; ss << "deformed/i" << framenum++ << ".jpg"; 37 | // char buf[255]; 38 | // sprintf(buf, "deformed/i%05d.jpg",framenum++); 39 | // imwrite(buf, visualized_curve); 40 | 41 | waitKey(1); 42 | } 43 | 44 | void onMouse( int event, int x, int y, int flags, void* ) 45 | { 46 | static Point2d start_touch(-1,-1); 47 | static Point2d last_touch(-1,-1); 48 | static int touch_control_point = -1; 49 | 50 | Point2d touch(x,y); 51 | if( event == CV_EVENT_LBUTTONDOWN ) { 52 | // cout << "mouse down\n"; 53 | start_touch = last_touch = touch; 54 | const vector& ctrl_pts = smls.GetDeformedControlPts(); 55 | for (int i=0; i= 0) { 62 | cout << "selected point " << touch_control_point << endl; 63 | } 64 | } else if( event == CV_EVENT_LBUTTONUP ) { 65 | // cout << "mouse up\n"; 66 | touch_control_point = -1; 67 | } else if (event == CV_EVENT_MOUSEMOVE && flags == CV_EVENT_FLAG_LBUTTON) { 68 | if (touch_control_point >= 0) { 69 | // cout << "mouse drag\n"; 70 | vector& def_ctrl_pts = smls.GetDeformedControlPts(); 71 | def_ctrl_pts[touch_control_point] += touch - last_touch; 72 | last_touch = touch; 73 | 74 | MLSUpdate(); 75 | } 76 | } 77 | 78 | } 79 | 80 | void onTrackbar(int, void*) 81 | { 82 | MLSUpdate(); 83 | } 84 | 85 | void MLSDeformCurve(const Mat& src, 86 | const vector& a_p2d, 87 | const vector >& stringrep 88 | ) 89 | { 90 | //Get extrema as control points 91 | vector control_pts; 92 | for(int i=0;i a; 118 | GetCurveForImage(src1, a, false); 119 | 120 | //move curve a bit to the middle, and scale up 121 | cv::transform(a,a,getRotationMatrix2D(Point2f(0,0),0,1.3)); 122 | // Mat tmp_curve_m(a); tmp_curve_m += Scalar(100,95); 123 | 124 | vector a_p2d, a_p2d_smoothed; 125 | ConvertCurve(a, a_p2d); 126 | 127 | //Get curvature extrema points 128 | vector > stringrep = CurvatureExtrema(a_p2d, a_p2d_smoothed,0.05,4.0); 129 | 130 | //Start interactive deformation 131 | src1.create(Size(700,600), CV_8UC3); 132 | MLSDeformCurve(src1,a_p2d,stringrep); 133 | } -------------------------------------------------------------------------------- /SchaeferMLS.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SchaeferMLS.h 3 | * CurveMatching 4 | * 5 | * Created by Roy Shilkrot on 12/28/12. 6 | * Copyright 2012 MIT. All rights reserved. 7 | * 8 | * Implementing "Image deformation using moving least squares", S. Schaefer et al. 2006 9 | * http://dl.acm.org/citation.cfm?id=1141920 10 | * (for 2D curves) 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | #include "CurveCSS.h" 18 | 19 | #define P2V(p) Vec2d((p).x,(p).y) 20 | #define V2P(v) Point2d((v)[0],(v)[1]) 21 | 22 | template 23 | class SchaeferMLS { 24 | Mat_ A; 25 | vector > As; 26 | vector mu_s; 27 | vector mu_r; 28 | vector > control_pts; 29 | vector > deformed_control_pts; 30 | vector > m_curve; 31 | vector > m_original_curve; 32 | double m_original_curve_scale; 33 | vector > m_w; 34 | vector m_vmp; 35 | 36 | template 37 | vector GetCurveAroundMean(const vector >& p, const Point_& p_star) { 38 | vector p_hat(p.size()); 39 | for (int i=0; i& ctrl_pts_curve_idx) { 58 | m_w.resize(m_curve.size()); 59 | for (int v=0; v 73 | Point2d GetWeightedMeanForPoint(int v, const vector >& q) { 74 | Point2d q_star(0,0); 75 | double sum_w_i = 0.0; 76 | for (int i=0; i& p_hat, const vector& w) { 85 | double mean = 0; 86 | for (int i=0; i 95 | Matx22d GetWeightedCovarMat(const vector >& p_hat, const vector& w) { 96 | Matx22d covarmat(0.0); 97 | for (int i=0; i >& curve, const vector& control_idx) { 106 | As.clear(); 107 | mu_s.clear(); 108 | mu_r.clear(); 109 | control_pts.clear(); 110 | deformed_control_pts.clear(); 111 | m_curve.clear(); 112 | m_original_curve.clear(); 113 | m_w.clear(); 114 | m_vmp.clear(); 115 | 116 | m_curve = curve; 117 | m_original_curve = curve; 118 | 119 | Mat a_ = Mat(m_original_curve).reshape(1); 120 | PCA a_pca(a_,Mat(),CV_PCA_DATA_AS_ROW); 121 | m_original_curve_scale = sqrt(a_pca.eigenvalues.at(0)); 122 | 123 | 124 | vector p; 125 | for (int i=0; i(control_idx.size())); 136 | m_vmp.resize(m_curve.size()); 137 | 138 | for (int i=0; i p_hat = GetCurveAroundMean(p,p_star); 143 | 144 | //Affine - section 2.1 145 | for (int j=0; j q; ConvertCurve(deformed_control_pts, q); 167 | 168 | for (int i=0; i q_hat = GetCurveAroundMean(q,q_star); 171 | 172 | // cout << A.row(i) << endl; 173 | Point2d newpoint(0,0); 174 | for (int j=0; j(newpoint.x,newpoint.y); 180 | } 181 | } 182 | 183 | void UpdateSimilarity() { 184 | vector q; ConvertCurve(deformed_control_pts, q); 185 | 186 | for (int i=0; i w; 188 | Point2d q_star = GetWeightedMeanForPoint(i,q); 189 | vector q_hat = GetCurveAroundMean(q,q_star); 190 | 191 | Point2d newpoint(0,0); 192 | for (int j=0; j(newpoint.x,newpoint.y); 201 | } 202 | } 203 | 204 | void UpdateRigid() { 205 | vector q; ConvertCurve(deformed_control_pts, q); 206 | 207 | for (int i=0; i w; 209 | Point2d q_star = GetWeightedMeanForPoint(i,q); 210 | vector q_hat = GetCurveAroundMean(q,q_star); 211 | 212 | Point2d newpoint(0,0); 213 | 214 | //calc sum_i(q_hat[i] * A[i]) 215 | for (int j=0; j(newpoint.x,newpoint.y); 227 | } 228 | } 229 | 230 | const vector >& GetControlPts() { return control_pts; } 231 | vector >& GetDeformedControlPts() { return deformed_control_pts; } 232 | 233 | void Draw(Mat& img) { 234 | // img.setTo(0); 235 | { 236 | //draw small original 237 | vector tmp_curve; 238 | cv::transform(m_original_curve,tmp_curve,getRotationMatrix2D(Point2f(0,0),0,50/m_original_curve_scale)); 239 | // Mat tmp_curve_m(tmp_curve); tmp_curve_m += Scalar(25,0); 240 | 241 | drawOpenCurve(img, tmp_curve, Scalar::all(255), 2); 242 | } 243 | drawOpenCurve(img, m_curve, Scalar(0,0,255), 2); 244 | for (int i=0; i 12 | void PolyLineSplit(const vector >& pl,vector& contourx, vector& contoury) { 13 | contourx.resize(pl.size()); 14 | contoury.resize(pl.size()); 15 | 16 | for (int j=0; j 24 | void PolyLineMerge(vector >& pl, const vector& contourx, const vector& contoury) { 25 | assert(contourx.size()==contoury.size()); 26 | pl.resize(contourx.size()); 27 | for (int j=0; j 34 | void ConvertCurve(const vector >& curve, vector >& output) { 35 | output.clear(); 36 | for (int j=0; j(curve[j].x,curve[j].y)); 38 | } 39 | } 40 | 41 | void ResampleCurve(const vector& curvex, const vector& curvey, 42 | vector& resampleX, vector& resampleY, 43 | int N, bool isOpen = false 44 | ); 45 | 46 | template 47 | void ResampleCurve(const vector >& curve, vector >& output, 48 | int N, bool isOpen = false 49 | ) 50 | { 51 | vector curvex, curvey, resampleX, resampleY; 52 | PolyLineSplit(curve, curvex, curvey); 53 | ResampleCurve(curvex,curvey,resampleX,resampleY,N,isOpen); 54 | PolyLineMerge(output, resampleX, resampleY); 55 | } 56 | 57 | template 58 | void drawOpenCurve(Mat& img, const vector >& curve, Scalar color, int thickness) { 59 | vector curve2i; 60 | ConvertCurve(curve, curve2i); 61 | for (int i=0; i 67 | void fillCurve(Mat& img, const vector >& curve, Scalar color) { 68 | vector curve2i; 69 | ConvertCurve(curve, curve2i); 70 | Point* pptr = &(curve2i[0]); 71 | const Point** ppptr = (const Point**)&(pptr); 72 | int psize = curve2i.size(); 73 | fillPoly(img, ppptr, &psize, 1, color); 74 | } 75 | 76 | #pragma mark CSS Image 77 | 78 | void ComputeCurveCSS(const vector& curvex, 79 | const vector& curvey, 80 | vector& kappa, 81 | vector& smoothX,vector& smoothY, 82 | double sigma = 1.0, 83 | bool isOpen = false); 84 | 85 | template 86 | void ComputeCurveCSS(const vector >& curve, 87 | vector& kappa, 88 | vector >& smooth, 89 | double sigma, 90 | bool isOpen = false 91 | ) 92 | { 93 | vector contourx(curve.size()),contoury(curve.size()); 94 | PolyLineSplit(curve, contourx, contoury); 95 | 96 | vector smoothx, smoothy; 97 | ComputeCurveCSS(contourx, contoury, kappa, smoothx, smoothy, sigma, isOpen); 98 | 99 | PolyLineMerge(smooth, smoothx, smoothy); 100 | } 101 | 102 | vector FindCSSInterestPoints(const vector& kappa); 103 | 104 | vector ComputeCSSImageMaximas(const vector& contourx_, const vector& contoury_, 105 | vector& contourx, vector& contoury, bool isClosedCurve = true); 106 | 107 | void SmoothCurve(const vector& curvex, 108 | const vector& curvey, 109 | vector& smoothX, 110 | vector& smoothY, 111 | vector& X, 112 | vector& XX, 113 | vector& Y, 114 | vector& YY, 115 | double sigma, 116 | bool isOpen = false); 117 | 118 | template 119 | void SimpleSmoothCurve(const vector >& curve, 120 | vector >& smooth, 121 | double sigma, 122 | bool isOpen = false) { 123 | vector contourx(curve.size()),contoury(curve.size()); 124 | PolyLineSplit(curve, contourx, contoury); 125 | 126 | vector smoothx, smoothy, X,XX,Y,YY; 127 | SmoothCurve(contourx, contoury, smoothx, smoothy, X,XX,Y,YY, sigma, isOpen); 128 | 129 | PolyLineMerge(smooth, smoothx, smoothy); 130 | } 131 | 132 | 133 | #pragma mark Curve Segments 134 | 135 | template 136 | void GetCurveSegments(const vector >& curve, const vector& interestPoints, vector > >& segments, bool closedCurve = true) { 137 | if (closedCurve) { 138 | segments.resize(interestPoints.size()); 139 | } else { 140 | segments.resize(interestPoints.size()+1); 141 | } 142 | 143 | for (int i = (closedCurve)?0:1; i(curve[j].x,curve[j].y)); 148 | } 149 | } 150 | if (closedCurve) { 151 | //put in the segment that passes the 0th point 152 | segments.back().clear(); 153 | for (int j=interestPoints.back(); j(curve[j].x,curve[j].y)); 155 | } 156 | for (int j=0; j(curve[j].x,curve[j].y)); 158 | } 159 | } else { 160 | //put in the segment after the last point 161 | segments.back().clear(); 162 | for (int j=interestPoints.back(); j(curve[j].x,curve[j].y)); 164 | } 165 | //put in the segment before the 1st point 166 | segments.front().clear(); 167 | for (int j=0; j(curve[j].x,curve[j].y)); 169 | } 170 | } 171 | for (int i=0; i x,y; 173 | cout <<"segments[i].size() " << segments[i].size() << endl; 174 | PolyLineSplit(segments[i], x, y); ResampleCurve(x, y, x, y, 50,true); PolyLineMerge(segments[i], x, y); 175 | } 176 | } 177 | template 178 | void GetCurveSegmentsWithCSSImage(vector >& curve, vector& interestPoints, vector > >& segments, bool closedCurve = true) { 179 | vector contourx(curve.size()),contoury(curve.size()); 180 | PolyLineSplit(curve, contourx, contoury); 181 | 182 | vector smoothx, smoothy; 183 | interestPoints = ComputeCSSImageMaximas(contourx, contoury, smoothx, smoothy); 184 | 185 | PolyLineMerge(curve, smoothx, smoothy); 186 | 187 | double minx,maxx; minMaxLoc(smoothx, &minx, &maxx); 188 | double miny,maxy; minMaxLoc(smoothy, &miny, &maxy); 189 | Mat drawing(maxy,maxx,CV_8UC3,Scalar(0)); 190 | RNG rng(time(NULL)); 191 | Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) ); 192 | // vector > > contours(1,curve); 193 | // drawContours( drawing, contours, 0, color, 2, 8); 194 | drawOpenCurve(drawing, curve, color, 2); 195 | 196 | for (int m=0; m& x, const vector& y); 208 | template 209 | double CalcCrossCorrelation(const Mat_& x, const Mat_& y) { 210 | vector xv; 211 | if (x.rows == 1) x.convertTo(xv, CV_64F); 212 | else if (x.cols == 1) Mat(x.t()).convertTo(xv, CV_64F); 213 | vector yv; 214 | if (y.rows == 1) y.convertTo(yv, CV_64F); 215 | else if (y.cols == 1) Mat(y.t()).convertTo(yv, CV_64F); 216 | return CalcCrossCorrelation(xv,yv); 217 | } 218 | 219 | 220 | double MatchTwoSegments(const vector& a, const vector& b); 221 | double MatchCurvesSmithWaterman(const vector >& a, const vector >& b, vector& traceback); 222 | double AdaptedMatchCurvesSmithWaterman(const vector >& a, const vector >& b, vector& traceback); 223 | -------------------------------------------------------------------------------- /CurveSignature.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CurveSignature.h 3 | * CurveMatching 4 | * 5 | * Created by Roy Shilkrot on 12/7/12. 6 | * Copyright 2012 MIT. All rights reserved. 7 | * 8 | */ 9 | 10 | #ifdef HAVE_MATHGL 11 | #include 12 | #include 13 | #endif 14 | 15 | #include 16 | 17 | #pragma mark Utilities 18 | 19 | #define CV_PROFILE(msg,code) \ 20 | {\ 21 | std::cout << msg << " ";\ 22 | double __time_in_ticks = (double)cv::getTickCount();\ 23 | { code }\ 24 | std::cout << "DONE " << ((double)cv::getTickCount() - __time_in_ticks)/cv::getTickFrequency() << "s" << std::endl;\ 25 | } 26 | 27 | #define CV_PROFILE(code) \ 28 | {\ 29 | std::cout << #code << " ";\ 30 | double __time_in_ticks = (double)cv::getTickCount();\ 31 | { code }\ 32 | std::cout << "DONE " << ((double)cv::getTickCount() - __time_in_ticks)/cv::getTickFrequency() << "s" << std::endl;\ 33 | } 34 | 35 | template 36 | Mat_ Find2DRigidTransform(const vector >& a, const vector >& b) { 37 | //use PCA to find relational scale 38 | Mat a_ = Mat(a).reshape(1); 39 | Mat b_ = Mat(b).reshape(1); 40 | PCA a_pca(a_,Mat(),CV_PCA_DATA_AS_ROW), b_pca(b_,Mat(),CV_PCA_DATA_AS_ROW); 41 | double s = sqrt(b_pca.eigenvalues.at(0)) / sqrt(a_pca.eigenvalues.at(0)); 42 | // cout << a_pca.eigenvectors << endl << a_pca.eigenvalues << endl << a_pca.mean << endl; 43 | // cout << b_pca.eigenvectors << endl << b_pca.eigenvalues << endl << b_pca.mean << endl; 44 | 45 | //convert to matrices and subtract mean 46 | Mat_ P(a.size(),2),Q(b.size(),2); 47 | Scalar a_m = mean(Mat(a)), b_m = mean(Mat(b)); 48 | for (int i=0; i A = P.t() * Q; 53 | SVD svd(A); 54 | Mat_ C = svd.vt.t() * svd.u.t(); 55 | double d = (determinant(C) > 0) ? 1 : -1; 56 | Mat_ R = svd.vt.t() * (Mat_(2,2) << 1,0, 0,d) * svd.u.t(); 57 | Mat_ T = (Mat_(3,3) << 1,0,b_m[0]/s, 0,1,b_m[1]/s, 0,0,1) * 58 | (Mat_(3,3) << s,0,0, 0,s,0, 0,0,s) * 59 | (Mat_(3,3) << R(0,0),R(0,1),0, R(1,0),R(1,1),0, 0,0,1) * 60 | (Mat_(3,3) << 1,0,-a_m[0], 0,1,-a_m[1], 0,0,1) 61 | ; 62 | return T(Range(0,2),Range::all()); 63 | } 64 | 65 | template 66 | Mat_ ConvertToMat(const vector >& mnt_DB) { 67 | Mat_ mnt_DB_m(mnt_DB.size(),mnt_DB[0].size()); 68 | for (int i=0; i 77 | void drawCurvePoints(Mat& img, const vector >& curve_, const Scalar& color, int thickness) { 78 | vector curve; 79 | ConvertCurve(curve_, curve); 80 | for (int i=0; i& curve, bool onlyUpper = true, bool getLower = false); 86 | 87 | template 88 | void imshow_(const std::string& str, const Mat& img) { 89 | Mat big; resize(img,big,Size(x,y)); 90 | imshow(str,big); 91 | } 92 | 93 | template 94 | vector > YnormalizedCurve(const vector >& curve) { 95 | vector curvex,curvey; PolyLineSplit(curve, curvex, curvey); 96 | double minval,maxval; 97 | minMaxIdx(curvey, &minval,&maxval); 98 | vector > curveout; 99 | for (int i=0; i(i,(curvey[i] - minval) / (maxval - minval))); 101 | } 102 | return curveout; 103 | } 104 | 105 | static int mgl_id = 0; 106 | 107 | template 108 | void ShowMathMLCurve(const vector > a_canon, const std::string& title) 109 | { 110 | #ifdef HAVE_MATHGL 111 | mglGraph gr; 112 | 113 | vector a_canon_x,a_canon_y; 114 | PolyLineSplit(a_canon, a_canon_x, a_canon_y); 115 | 116 | mglData mgl_a_x(&(a_canon_x[0]),a_canon_x.size()),mgl_a_y(&(a_canon_y[0]),a_canon_y.size()); 117 | 118 | gr.Title(title.c_str()); 119 | gr.Aspect(1, 1); 120 | double axmin,axmax,aymin,aymax; 121 | minMaxIdx(a_canon_x, &axmin, &axmax); 122 | minMaxIdx(a_canon_y, &aymin, &aymax); 123 | gr.SetRanges(axmin, axmax, aymin, aymax); 124 | gr.Axis(); 125 | gr.Grid(); 126 | gr.Plot(mgl_a_x,mgl_a_y); 127 | 128 | Mat img(gr.GetHeight(),gr.GetWidth(),CV_8UC3,(void*)gr.GetRGB()); 129 | stringstream ss1; ss1 << "MathMLCurves " << mgl_id; mgl_id++; 130 | imshow(ss1.str(), img); 131 | #endif 132 | } 133 | 134 | template 135 | void ShowMathMLCurve(const vector a_canon, const std::string& title) 136 | { 137 | vector count_; for(int x=0;x a_p2d; PolyLineMerge(a_p2d, count_, a_canon); 139 | ShowMathMLCurve(a_p2d,title); 140 | } 141 | 142 | template 143 | void ShowMathMLCurves(const vector > a_canon, const vector >& b_canon, const std::string& title) 144 | { 145 | #ifdef HAVE_MATHGL 146 | mglGraph gr; 147 | 148 | vector a_canon_x,a_canon_y; 149 | PolyLineSplit(a_canon, a_canon_x, a_canon_y); 150 | vector b_canon_x,b_canon_y; 151 | PolyLineSplit(b_canon, b_canon_x, b_canon_y); 152 | 153 | mglData mgl_a_x(&(a_canon_x[0]),a_canon_x.size()),mgl_a_y(&(a_canon_y[0]),a_canon_y.size()); 154 | mglData mgl_b_x(&(b_canon_x[0]),b_canon_x.size()),mgl_b_y(&(b_canon_y[0]),b_canon_y.size()); 155 | 156 | gr.Title(title.c_str()); 157 | gr.Aspect(1, 1); 158 | double axmin,axmax,aymin,aymax,bxmin,bxmax,bymin,bymax; 159 | minMaxIdx(a_canon_x, &axmin, &axmax); 160 | minMaxIdx(a_canon_y, &aymin, &aymax); 161 | minMaxIdx(b_canon_x, &bxmin, &bxmax); 162 | minMaxIdx(b_canon_y, &bymin, &bymax); 163 | gr.SetRanges(min(axmin,bxmin), max(axmax,bxmax), min(aymin,bymin), max(aymax, bymax)); 164 | gr.Axis(); 165 | gr.Grid(); 166 | gr.Plot(mgl_a_x,mgl_a_y); 167 | gr.Plot(mgl_b_x,mgl_b_y); 168 | 169 | Mat img(gr.GetHeight(),gr.GetWidth(),CV_8UC3,(void*)gr.GetRGB()); 170 | double cc = CalcCrossCorrelation(a_canon_y, b_canon_y); 171 | stringstream ss; ss << "cross correlation " << cc; 172 | putText(img, ss.str(), Point(10,20), CV_FONT_NORMAL, 1.0, Scalar(255), 2); 173 | stringstream ss1; ss1 << "MathMLCurves " << mgl_id; mgl_id++; 174 | imshow(ss1.str(), img); 175 | #endif 176 | } 177 | 178 | template 179 | void ShowMathMLCurves(const vector a_canon, const vector& b_canon, const std::string& title) 180 | { 181 | assert(a_canon.size() == b_canon.size()); 182 | vector count_; for(int x=0;x a_p2d; PolyLineMerge(a_p2d, count_, a_canon); 184 | vector b_p2d; PolyLineMerge(b_p2d, count_, b_canon); 185 | ShowMathMLCurves(a_p2d, 186 | b_p2d, 187 | title); 188 | } 189 | 190 | #pragma mark Curvature Extrema 191 | 192 | template 193 | vector > CurvatureExtrema(const vector >& curve, vector >& smooth, double cutoff = 0.025, double smooth_sigma = 4.0) { 194 | vector kappa1; 195 | ComputeCurveCSS(curve, kappa1, smooth, smooth_sigma, true); 196 | 197 | ShowMathMLCurve(kappa1, "curvature"); 198 | 199 | vector > stringrep; 200 | for (int i=1; i 0 && kappa1[i] < 0)) 203 | // { 204 | // stringrep.push_back(make_pair('I',i)); 205 | // } 206 | // if ((kappa1[i-1] < 0 && kappa1[i] > 0)) { 207 | // stringrep.push_back(make_pair('i',i)); 208 | // } 209 | 210 | //extrema 211 | if (abs(kappa1[i]) < cutoff) continue; 212 | if (kappa1[i] > 0) { 213 | if (kappa1[i-1] < kappa1[i] && kappa1[i+1] < kappa1[i]) { 214 | stringrep.push_back(make_pair('X',i)); 215 | } 216 | if (kappa1[i-1] > kappa1[i] && kappa1[i+1] > kappa1[i]) { 217 | stringrep.push_back(make_pair('N',i)); 218 | } 219 | } else { 220 | if (kappa1[i-1] > kappa1[i] && kappa1[i+1] > kappa1[i]) { 221 | stringrep.push_back(make_pair('x',i)); 222 | } 223 | if (kappa1[i-1] < kappa1[i] && kappa1[i+1] < kappa1[i]) { 224 | stringrep.push_back(make_pair('n',i)); 225 | } 226 | } 227 | } 228 | 229 | return stringrep; 230 | } 231 | 232 | template 233 | void VisualizeExtremaPoints(Mat& outout, 234 | const vector >& smooth, 235 | const vector >& stringrep) 236 | { 237 | for (int i=0; i 260 | void VisualizeExtrema(const Mat& src, 261 | const vector >& smooth, 262 | const vector >& stringrep) 263 | { 264 | static int win_id = 0; 265 | 266 | Mat outout(src.size(),CV_8UC3,Scalar::all(0)); 267 | drawOpenCurve(outout, smooth, Scalar(255), 2); 268 | VisualizeExtremaPoints(outout,smooth,stringrep); 269 | stringstream ss; 270 | for (int i=0; i& curve, vector >& DB, vector& DB_params); 282 | 283 | template 284 | void PrepareSignatureDB(const vector >& curve, vector >& DB, vector& DB_params) { 285 | vector curved; ConvertCurve(curve, curved); 286 | PrepareSignatureDB(curved,DB,DB_params); 287 | } 288 | 289 | void CompareCurvesUsingFLANN(const vector& DB, 290 | const vector >& query_DB, 291 | int& a_id, 292 | int& a_subset_id, 293 | int& b_subset_id); 294 | 295 | void CompareCurvesUsingSignatureDBMatcher(FlannBasedMatcher& matcher, 296 | const vector& typical_params, 297 | const vector >& b_DB, 298 | int& a_id, 299 | int& a_len, 300 | int& a_off, 301 | int& b_len, 302 | int& b_off, 303 | double& score 304 | ); 305 | 306 | void CompareCurvesUsingSignatureDB(const vector& a_DB_params, 307 | const vector& b_DB_params, 308 | const vector >& a_DB, 309 | const vector >& b_DB, 310 | int& a_len, 311 | int& a_off, 312 | int& b_len, 313 | int& b_off, 314 | double& score 315 | ); 316 | 317 | void CompareCurvesUsingSignatureDB(const vector& a, 318 | const vector& b, 319 | int& a_len, 320 | int& a_off, 321 | int& b_len, 322 | int& b_off, 323 | double& score 324 | ); 325 | 326 | template 327 | void CompareCurvesUsingSignatureDB(const vector >& a, 328 | const vector >& b, 329 | int& a_len, 330 | int& a_off, 331 | int& b_len, 332 | int& b_off, 333 | double& score 334 | ) { 335 | vector ad; ConvertCurve(a, ad); 336 | vector bd; ConvertCurve(b, bd); 337 | CompareCurvesUsingSignatureDB(ad,bd,a_len,a_off,b_len,b_off,score); 338 | } 339 | 340 | #pragma mark Heat Kernel Signature computing and matching 341 | 342 | template 343 | void CurveSignature(const vector >& curve_T, Mat_& hks, int sigmas) { 344 | vector curved; ConvertCurve(curve_T, curved); 345 | vector curve; 346 | // if(curved.size() != 200) 347 | // ResampleCurve(curved, curve, 200); 348 | // else 349 | curve = curved; 350 | 351 | vector smooth; 352 | 353 | Mat_ img(sigmas*10,curve.size(),Vec3b(0,0,0)); 354 | hks.create(sigmas*10,curve.size()); 355 | for (int i=0; i kappa(hks.cols,0.0); 358 | ComputeCurveCSS(curve, kappa, smooth, sigma, true); 359 | Mat(Mat(kappa).t()).copyTo(hks.row(i)); 360 | } 361 | 362 | // imshow_<800,200>("HKS(x,sigma)",hks); 363 | // waitKey(); 364 | } 365 | 366 | void CompareSignatures(const Mat_& a, const Mat_& b); 367 | 368 | #ifndef WITHOUT_OPENCL 369 | void CompareCurvesGPU(const vector& a, 370 | const vector& b, 371 | double& a_sigma, 372 | double& b_sigma, 373 | int& a_offset, 374 | int& b_offset, 375 | int& len 376 | ); 377 | 378 | template 379 | void CompareCurvesGPU(const vector >& a_, 380 | const vector >& b_, 381 | double& a_sigma, 382 | double& b_sigma, 383 | int& a_offset, 384 | int& b_offset, 385 | int& len 386 | ) 387 | { 388 | vector a; ConvertCurve(a_, a); 389 | vector b; ConvertCurve(b_, b); 390 | CompareCurvesGPU(a, b, 391 | a_sigma, b_sigma, 392 | a_offset, b_offset, 393 | len 394 | ); 395 | } 396 | 397 | 398 | void CompareSignaturesGPU(const Mat_& a, const Mat_& b, 399 | double& a_sigma, 400 | double& b_sigma, 401 | int& a_offset, 402 | int& b_offset, 403 | int& len 404 | ); 405 | #endif -------------------------------------------------------------------------------- /CurveCSS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CurveCSS.cpp 3 | * CurveMatching 4 | * 5 | * Created by Roy Shilkrot on 11/28/12. 6 | * 7 | */ 8 | 9 | #include "std.h" 10 | 11 | using namespace cv; 12 | 13 | #include "CurveCSS.h" 14 | 15 | #ifdef HAVE_MATHGL 16 | #include 17 | #include 18 | #endif 19 | 20 | #define TwoPi 6.28318530718 21 | 22 | #pragma mark Gaussian Smoothing and Curvature 23 | 24 | /* 1st and 2nd derivative of 1D gaussian 25 | */ 26 | void getGaussianDerivs(double sigma, int M, vector& gaussian, vector& dg, vector& d2g) { 27 | // static double sqrt_two_pi = sqrt(TwoPi); 28 | int L; 29 | if (sigma < 0) { 30 | M = 1; 31 | L = 0; 32 | dg.resize(M); d2g.resize(M); gaussian.resize(M); 33 | gaussian[0] = dg[0] = d2g[0] = 1.0; 34 | return; 35 | } 36 | 37 | L = (M - 1) / 2; 38 | dg.resize(M); d2g.resize(M); gaussian.resize(M); 39 | getGaussianKernel(M, sigma, CV_64F).copyTo(Mat(gaussian)); 40 | 41 | double sigma_sq = sigma * sigma; 42 | double sigma_quad = sigma_sq*sigma_sq; 43 | for (double i = -L; i < L+1.0; i += 1.0) { 44 | int idx = (int)(i+L); 45 | // from http://www.cedar.buffalo.edu/~srihari/CSE555/Normal2.pdf 46 | dg[idx] = (-i/sigma_sq) * gaussian[idx]; 47 | d2g[idx] = (-sigma_sq + i*i)/sigma_quad * gaussian[idx]; 48 | } 49 | } 50 | 51 | /* 1st and 2nd derivative of smoothed curve point */ 52 | void getdX(vector x, 53 | int n, 54 | double sigma, 55 | double& gx, 56 | double& dgx, 57 | double& d2gx, 58 | const vector& g, 59 | const vector& dg, 60 | const vector& d2g, 61 | bool isOpen = false) 62 | { 63 | int L = (g.size() - 1) / 2; 64 | 65 | gx = dgx = d2gx = 0.0; 66 | // cout << "Point " << n << ": "; 67 | for (int k = -L; k < L+1; k++) { 68 | double x_n_k; 69 | if (n-k < 0) { 70 | if (isOpen) { 71 | //open curve - 72 | //mirror values on border 73 | // x_n_k = x[-(n-k)]; 74 | //stretch values on border 75 | x_n_k = x.front(); 76 | } else { 77 | //closed curve - take values from end of curve 78 | x_n_k = x[x.size()+(n-k)]; 79 | } 80 | } else if(n-k > x.size()-1) { 81 | if (isOpen) { 82 | //mirror value on border 83 | // x_n_k = x[n+k]; 84 | //stretch value on border 85 | x_n_k = x.back(); 86 | } else { 87 | x_n_k = x[(n-k)-(x.size())]; 88 | } 89 | } else { 90 | // cout << n-k; 91 | x_n_k = x[n-k]; 92 | } 93 | // cout << "* g[" << g[k + L] << "], "; 94 | 95 | gx += x_n_k * g[k + L]; //gaussians go [0 -> M-1] 96 | dgx += x_n_k * dg[k + L]; 97 | d2gx += x_n_k * d2g[k + L]; 98 | } 99 | // cout << endl; 100 | } 101 | 102 | 103 | /* 0th, 1st and 2nd derivatives of whole smoothed curve */ 104 | void getdXcurve(vector x, 105 | double sigma, 106 | vector& gx, 107 | vector& dx, 108 | vector& d2x, 109 | const vector& g, 110 | const vector& dg, 111 | const vector& d2g, 112 | bool isOpen = false) 113 | { 114 | gx.resize(x.size()); 115 | dx.resize(x.size()); 116 | d2x.resize(x.size()); 117 | for (int i=0; i& curvex, const vector& curvey, 126 | vector& resampleX, vector& resampleY, 127 | int N, 128 | bool isOpen 129 | ) { 130 | assert(curvex.size()>0 && curvey.size()>0 && curvex.size()==curvey.size()); 131 | 132 | vector resamplepl(N); resamplepl[0].x = curvex[0]; resamplepl[0].y = curvey[0]; 133 | vector pl; PolyLineMerge(pl,curvex,curvey); 134 | 135 | double pl_length = arcLength(pl, false); 136 | double resample_size = pl_length / (double)N; 137 | int curr = 0; 138 | double dist = 0.0; 139 | for (int i=1; i= resample_size) { 145 | //put a point on line 146 | double _d = last_dist - (dist-resample_size); 147 | Point2d cp(pl[curr].x,pl[curr].y),cp1(pl[curr+1].x,pl[curr+1].y); 148 | Point2d dirv = cp1-cp; dirv = dirv * (1.0 / norm(dirv)); 149 | // cout << "point " << i << " between " << curr << " and " << curr+1 << " remaining " << dist << endl; 150 | assert(i < resamplepl.size()); 151 | resamplepl[i] = cp + dirv * _d; 152 | i++; 153 | 154 | dist = last_dist - _d; //remaining dist 155 | 156 | //if remaining dist to next point needs more sampling... (within some epsilon) 157 | while (dist - resample_size > 1e-3) { 158 | // cout << "point " << i << " between " << curr << " and " << curr+1 << " remaining " << dist << endl; 159 | assert(i < resamplepl.size()); 160 | resamplepl[i] = resamplepl[i-1] + dirv * resample_size; 161 | dist -= resample_size; 162 | i++; 163 | } 164 | } 165 | 166 | curr++; 167 | } 168 | 169 | PolyLineSplit(resamplepl,resampleX,resampleY); 170 | } 171 | 172 | #pragma mark CSS image 173 | 174 | void SmoothCurve(const vector& curvex, 175 | const vector& curvey, 176 | vector& smoothX, 177 | vector& smoothY, 178 | vector& X, 179 | vector& XX, 180 | vector& Y, 181 | vector& YY, 182 | double sigma, 183 | bool isOpen) 184 | { 185 | int M = round((10.0*sigma+1.0) / 2.0) * 2 - 1; 186 | // assert(M % 2 == 1); //M is an odd number 187 | 188 | vector g,dg,d2g; 189 | getGaussianDerivs(sigma,M,g,dg,d2g); 190 | 191 | 192 | getdXcurve(curvex,sigma,smoothX,X,XX,g,dg,d2g,isOpen); 193 | getdXcurve(curvey,sigma,smoothY,Y,YY,g,dg,d2g,isOpen); 194 | } 195 | 196 | /* compute curvature of curve after gaussian smoothing 197 | from "Shape similarity retrieval under affine transforms", Mokhtarian & Abbasi 2002 198 | curvex - x position of points 199 | curvey - y position of points 200 | kappa - curvature coeff for each point 201 | sigma - gaussian sigma 202 | */ 203 | void ComputeCurveCSS(const vector& curvex, 204 | const vector& curvey, 205 | vector& kappa, 206 | vector& smoothX, vector& smoothY, 207 | double sigma, 208 | bool isOpen 209 | ) 210 | { 211 | vector X,XX,Y,YY; 212 | SmoothCurve(curvex, curvey, smoothX, smoothY, X,XX,Y,YY, sigma, isOpen); 213 | 214 | kappa.resize(curvex.size()); 215 | for (int i=0; i FindCSSInterestPoints(const vector& kappa) { 223 | vector crossings; 224 | for (int i=0; i 0) || kappa[i] > 0 && kappa[i+1] < 0) { 226 | crossings.push_back(i); 227 | } 228 | } 229 | return crossings; 230 | } 231 | 232 | vector EliminateCloseMaximas(const vector& maximasv, map& maximas) { 233 | //eliminate degenerate segments (of very small length) 234 | vector maximasvv; 235 | for (int i=0;i maximas[maximasv[i+1]]) { 241 | maximasvv.push_back(maximasv[i]); 242 | } else { 243 | maximasvv.push_back(maximasv[i+1]); 244 | } 245 | i++; //skip next element as well 246 | } else { 247 | maximasvv.push_back(maximasv[i]); 248 | } 249 | } 250 | return maximasvv; 251 | } 252 | 253 | /* compute the CSS image */ 254 | vector ComputeCSSImageMaximas(const vector& contourx_, const vector& contoury_, 255 | vector& contourx, vector& contoury, 256 | bool isClosedCurve 257 | ) 258 | { 259 | ResampleCurve(contourx_, contoury_, contourx, contoury, 200, !isClosedCurve); 260 | vector pl; PolyLineMerge(pl,contourx,contoury); 261 | 262 | map maximas; 263 | 264 | Mat_ img(500,200,Vec3b(0,0,0)), contourimg(350,350,Vec3b(0,0,0)); 265 | bool done = false; 266 | //#pragma omp parallel for 267 | for (int i=0; i<490; i++) { 268 | if (!done) { 269 | double sigma = 1.0 + ((double)i)*0.1; 270 | vector kappa, smoothx, smoothy; 271 | ComputeCurveCSS(contourx, contoury, kappa, smoothx, smoothy,sigma); 272 | 273 | // vector > contours(1); 274 | // PolyLineMerge(contours[0], smoothx, smoothy); 275 | // contourimg = Vec3b(0,0,0); 276 | // drawContours(contourimg, contours, 0, Scalar(255,255,255), CV_FILLED); 277 | 278 | vector crossings = FindCSSInterestPoints(kappa); 279 | if (crossings.size() > 0) { 280 | for (int c=0; c::iterator itr = maximas.begin(); itr!=maximas.end(); ++itr) { 309 | if (max_sigma < (*itr).second) { 310 | max_sigma = (*itr).second; 311 | } 312 | } 313 | //get segments with largest sigma 314 | vector maximasv; 315 | for (map::iterator itr = maximas.begin(); itr!=maximas.end(); ++itr) { 316 | if ((*itr).second > max_sigma/8.0) { 317 | maximasv.push_back((*itr).first); 318 | } 319 | } 320 | //eliminate degenerate segments (of very small length) 321 | vector maximasvv = EliminateCloseMaximas(maximasv,maximas); //1st pass 322 | maximasvv = EliminateCloseMaximas(maximasvv,maximas); //2nd pass 323 | maximasv = maximasvv; 324 | for (vector::iterator itr = maximasv.begin(); itr!=maximasv.end(); ++itr) { 325 | cout << *itr << " - " << maximas[*itr] << endl; 326 | } 327 | // Mat zoom; resize(img,zoom,Size(img.rows*2,img.cols*2)); 328 | imshow("css image",img); 329 | waitKey(); 330 | return maximasv; 331 | } 332 | 333 | #pragma mark Curve Matching 334 | 335 | /* calculate the "centroid distance" for the curve */ 336 | void GetCurveSignature(const vector& a, vector& signature) { 337 | signature.resize(a.size()); 338 | Scalar a_mean = mean(a); Point2d a_mpt(a_mean[0],a_mean[1]); 339 | 340 | //centroid distance 341 | for (int i=0; i& x, const vector& y) { 348 | assert(x.size()==y.size()); 349 | int i,j,n = x.size(); 350 | double mx,my,sx,sy,sxy,denom,r; 351 | 352 | /* Calculate the mean of the two series x[], y[] */ 353 | mx = 0; 354 | my = 0; 355 | for (i=0;i= n) 379 | continue; 380 | else 381 | sxy += (x[i] - mx) * (y[j] - my); 382 | /* Or should it be (?) 383 | if (j < 0 || j >= n) 384 | sxy += (x[i] - mx) * (-my); 385 | else 386 | sxy += (x[i] - mx) * (y[j] - my); 387 | */ 388 | } 389 | r = sxy / denom; 390 | 391 | /* r is the correlation coefficient at "delay" */ 392 | } 393 | return r; 394 | } 395 | 396 | /* calculate the similarity score between two curve segments 397 | Mai 2010, "Affine-invariant shape matching and recognition under partial occlusion", section 4.1 398 | */ 399 | double MatchTwoSegments(const vector& a_, const vector& b_) { 400 | assert(a_.size() == b_.size()); //cross correlation will work only for similar length curves 401 | if(a_.size() <= 1 || b_.size() <= 1) { 402 | cerr << "degenerate: a_.size() " << a_.size() << " b_.size() " << b_.size() << endl; 403 | return -1.0; //check degenrate case 404 | } 405 | 406 | vector a_x(a_.size()),a_y(a_.size()),b_x(b_.size()),b_y(b_.size()); 407 | vector a_x_(a_.size()),a_y_(a_.size()),b_x_(b_.size()),b_y_(b_.size()); 408 | vector a = a_, b = b_; 409 | // PolyLineSplit(a_, a_x_, a_y_); ResampleCurve(a_x_, a_y_, a_x, a_y, 50); PolyLineMerge(a, a_x, a_y); 410 | // PolyLineSplit(b_, b_x_, b_y_); ResampleCurve(b_x_, b_y_, b_x, b_y, 50); PolyLineMerge(b, b_x, b_y); 411 | 412 | Scalar a_mean = mean(a), b_mean = mean(b); 413 | Point2d a_mpt(a_mean[0],a_mean[1]),b_mpt(b_mean[0],b_mean[1]); 414 | vector a_m(a.size()),b_m(b.size()); 415 | for (int i=0; i a_mM = Mat(a_m).reshape(1).t(); 419 | Mat_ b_mM = Mat(b_m).reshape(1).t(); 420 | SVD asvd(a_mM),bsvd(b_mM); 421 | vector a_canon(a.size()),b_canon(b.size()); 422 | Mat(asvd.vt.t()).copyTo(a_mM); 423 | a_mM.reshape(2).copyTo(Mat(a_canon)); 424 | Mat(bsvd.vt.t()).copyTo(b_mM); 425 | b_mM.reshape(2).copyTo(Mat(b_canon)); 426 | 427 | 428 | vector a_sig; GetCurveSignature(a_canon, a_sig); 429 | vector b_sig; GetCurveSignature(b_canon, b_sig); 430 | 431 | double cc = CalcCrossCorrelation(a_sig, b_sig); 432 | 433 | #if 0 434 | #ifdef HAVE_MATHGL 435 | { 436 | mglGraph gr; 437 | gr.SubPlot(2, 1, 0, ""); 438 | 439 | vector a_canon_x,a_canon_y; 440 | PolyLineSplit(a_canon, a_canon_x, a_canon_y); 441 | vector b_canon_x,b_canon_y; 442 | PolyLineSplit(b_canon, b_canon_x, b_canon_y); 443 | 444 | mglData mgl_a_x(&(a_canon_x[0]),a_canon_x.size()),mgl_a_y(&(a_canon_y[0]),a_canon_y.size()); 445 | mglData mgl_b_x(&(b_canon_x[0]),b_canon_x.size()),mgl_b_y(&(b_canon_y[0]),b_canon_y.size()); 446 | 447 | gr.Title("Canonical"); 448 | gr.Aspect(1, 1); 449 | gr.SetRanges(-.5, .5, -.5, .5); 450 | gr.Axis(); 451 | gr.Grid(); 452 | gr.Plot(mgl_a_x,mgl_a_y); 453 | gr.Plot(mgl_b_x,mgl_b_y); 454 | 455 | 456 | gr.SubPlot(2, 1, 1, ""); 457 | mglData x(&(a_sig[0]),a_sig.size()),x1(&(b_sig[0]),b_sig.size()); 458 | 459 | gr.Title("Signature"); 460 | gr.SetRanges(0, max(a_sig.size(),b_sig.size()), 0, 0.55); 461 | gr.Axis(); 462 | gr.Grid(); 463 | gr.Plot(x); 464 | gr.Plot(x1); 465 | 466 | Mat img(gr.GetHeight(),gr.GetWidth(),CV_8UC3,(void*)gr.GetRGB()); 467 | stringstream ss; ss << "cross correlation " << cc; 468 | putText(img, ss.str(), Point(10,20), CV_FONT_NORMAL, 1.0, Scalar(255), 2); 469 | imshow("tmp", img); 470 | waitKey(); 471 | } 472 | #endif 473 | #endif 474 | 475 | return cc; // > 0.8 ? cc : 0.0; 476 | } 477 | 478 | /* match the two curves using adapted Smith-Waterman aligning algorithm 479 | Mai 2010, "Affine-invariant shape matching and recognition under partial occlusion", section 4.2 */ 480 | 481 | Mat_ GetSmithWatermanHMatrix(const vector >& a, const vector >& b) { 482 | int M = a.size(); 483 | int N = b.size(); 484 | 485 | //Smith-Waterman 486 | Mat_ H(M,N-1,0.0); 487 | for (int i=1; i v(4,0.0); 490 | v[1] = H(i-1,j-1) + MatchTwoSegments(a[i], b[j]); 491 | v[2] = H(i-1,j) - 1.0; 492 | v[3] = H(i,j-1) - 1.0; 493 | H(i,j) = *(max_element(v.begin(), v.end())); 494 | } 495 | } 496 | cout << H << endl; 497 | return H; 498 | } 499 | 500 | /* original Smith Waterman algorithm */ 501 | double MatchCurvesSmithWaterman(const vector >& a, const vector >& b, vector& traceback) 502 | { 503 | Mat_ H = GetSmithWatermanHMatrix(a,b); 504 | Point maxp; double maxval; 505 | minMaxLoc(H, NULL, &maxval, NULL, &maxp); 506 | while (H(maxp.y,maxp.x) != 0) { 507 | // cout << "H(maxp.y-1,maxp.x-1) > H(maxp.y,maxp.x-1)" << H(maxp.y-1,maxp.x-1) << " > " << H(maxp.y,maxp.x-1) << endl; 508 | if (H(maxp.y-1,maxp.x-1) > H(maxp.y,maxp.x-1) && 509 | H(maxp.y-1,maxp.x-1) > H(maxp.y-1,maxp.x)) 510 | { 511 | maxp = maxp - Point(1,1); 512 | traceback.push_back(maxp); 513 | } else 514 | if (H(maxp.y-1,maxp.x) > H(maxp.y-1,maxp.x-1) && 515 | H(maxp.y-1,maxp.x) > H(maxp.y,maxp.x-1)) 516 | { 517 | maxp.y--; 518 | traceback.push_back(maxp); 519 | } else 520 | if (H(maxp.y,maxp.x-1) > H(maxp.y-1,maxp.x-1) && 521 | H(maxp.y,maxp.x-1) > H(maxp.y-1,maxp.x)) 522 | { 523 | maxp.x--; 524 | traceback.push_back(maxp); 525 | } 526 | else { 527 | break; 528 | } 529 | } 530 | for (int k=0; k "; 532 | } 533 | cout << endl; 534 | return maxval; 535 | } 536 | 537 | /* adapted Smith Waterman */ 538 | double AdaptedMatchCurvesSmithWaterman(const vector >& a, const vector >& b, vector& traceback) 539 | { 540 | int M = a.size(); 541 | int N = b.size(); 542 | 543 | Mat_ H = GetSmithWatermanHMatrix(a,b); 544 | 545 | vector > tracebacks; 546 | vector max_traceback; 547 | int max_traceback_len = 0; 548 | for (int i=M-1; i>=2; i--) { 549 | for (int j=N-2; j>=2; j--) { 550 | if (i < max_traceback_len || j < max_traceback_len) { 551 | continue; //skip it, it already can't be longer.. 552 | } 553 | 554 | //Traceback 555 | vector tmp_traceback; 556 | Point maxp = Point(i,j); 557 | tmp_traceback.push_back(maxp); 558 | // maxp = maxp - Point(1,1); 559 | // tmp_traceback.push_back(maxp); 560 | bool movedup = false,movedleft = false; 561 | while (H(maxp.y,maxp.x) != 0 && maxp.y > 1 && maxp.x > 1) { 562 | if (H(maxp.y-1,maxp.x-1) > H(maxp.y,maxp.x-1) && 563 | H(maxp.y-1,maxp.x-1) > H(maxp.y-1,maxp.x)) 564 | { 565 | // cout << "move left-up" << endl; 566 | maxp = maxp - Point(1,1); 567 | traceback.push_back(maxp); 568 | } else if (H(maxp.y-1,maxp.x) > H(maxp.y-1,maxp.x-1) && 569 | H(maxp.y-1,maxp.x) > H(maxp.y,maxp.x-1)) 570 | { 571 | // cout << "move up" << endl; 572 | maxp.y--; 573 | movedup = true; 574 | } else if (H(maxp.y,maxp.x-1) > H(maxp.y-1,maxp.x-1) && 575 | H(maxp.y,maxp.x-1) > H(maxp.y-1,maxp.x)) 576 | { 577 | // cout << "move left" << endl; 578 | maxp.x--; 579 | movedleft = true; 580 | } 581 | if (movedup && movedleft) { 582 | traceback.push_back(maxp); 583 | movedup = movedleft = false; 584 | } 585 | } 586 | for (int k=0; k "; 588 | } 589 | cout << endl; 590 | if (tmp_traceback.size() > max_traceback_len || 591 | ( 592 | tmp_traceback.size() == max_traceback_len && //if equal - look for highest match 593 | H(tmp_traceback.front().y,tmp_traceback.front().x) > H(max_traceback.front().y,max_traceback.front().x) 594 | ) 595 | ) 596 | { 597 | max_traceback_len = tmp_traceback.size(); 598 | max_traceback = tmp_traceback; 599 | cout << "taking traceback of length " << max_traceback_len << endl; 600 | } 601 | } 602 | } 603 | 604 | traceback = max_traceback; 605 | 606 | return H(traceback[0].y,traceback[0].x); 607 | } 608 | 609 | 610 | -------------------------------------------------------------------------------- /CurveSignature.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CurveSignature.cpp 3 | * CurveMatching 4 | * 5 | * Created by Roy Shilkrot on 12/7/12. 6 | * Copyright 2012 MIT. All rights reserved. 7 | * 8 | */ 9 | 10 | #include "std.h" 11 | #include 12 | #include 13 | #include 14 | using namespace cv; 15 | 16 | #include "CurveCSS.h" 17 | #include "CurveSignature.h" 18 | 19 | #ifndef WITHOUT_OPENCL 20 | #include "CLSignatureMatching.h" 21 | #endif 22 | 23 | #pragma mark Utilities 24 | 25 | void GetCurveForImage(const Mat& filename, vector& whole, vector& curve_upper, vector& curve_lower) { 26 | assert(!filename.empty()); 27 | Mat tmp = filename; 28 | Mat gray; 29 | if(tmp.type() == CV_8UC3) 30 | cvtColor(tmp, gray, CV_BGR2GRAY); 31 | else if(tmp.type() == CV_8UC1) 32 | gray = tmp; 33 | else 34 | cvError(-1, "GetCurveForImage", "unsupported image format", __FILE__, __LINE__); 35 | 36 | 37 | threshold(gray, gray, 128, 255, THRESH_BINARY); 38 | // imshow("input",gray); 39 | 40 | vector > contours; 41 | findContours( gray, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); 42 | if (contours.size()<=0) return; 43 | 44 | vector upperCurve = contours[0]; 45 | if (upperCurve.size() <= 50) { 46 | return; 47 | } 48 | 49 | //find minimal and maximal X coord 50 | vector x,y; 51 | PolyLineSplit(contours[0], x, y); 52 | Point minxp,maxxp; 53 | minMaxLoc(x, 0, 0, &minxp, &maxxp); 54 | int minx = minxp.x,maxx = maxxp.x; 55 | if (minx > maxx) swap(minx, maxx); 56 | 57 | //take lower and upper halves of the curve 58 | vector upper,lower; 59 | upper.insert(upper.begin(),contours[0].begin()+minx,contours[0].begin()+maxx); 60 | lower.insert(lower.begin(),contours[0].begin()+maxx,contours[0].end()); 61 | lower.insert(lower.end(),contours[0].begin(),contours[0].begin()+minx); 62 | 63 | //test which is really the upper part, by looking at the y-coord of the mid point 64 | 65 | if (lower[lower.size()/2].y <= upper[upper.size()/2].y) { 66 | curve_upper = lower; 67 | curve_lower = upper; 68 | } else { 69 | curve_upper = upper; 70 | curve_lower = lower; 71 | } 72 | 73 | //make sure it goes left-to-right 74 | if (curve_upper.front().x > curve_upper.back().x) { //hmmm, need to flip 75 | reverse(curve_upper.begin(), curve_upper.end()); 76 | } 77 | 78 | whole = upperCurve; 79 | } 80 | 81 | void GetCurveForImage(const Mat& filename, vector& curve, bool onlyUpper, bool getLower) { 82 | vector whole,upper,lower; 83 | GetCurveForImage(filename,whole,upper,lower); 84 | if (onlyUpper) { 85 | if (getLower) 86 | curve = lower; 87 | else 88 | curve = upper; 89 | } else { 90 | curve = whole; 91 | } 92 | } 93 | 94 | 95 | #pragma mark Signature Database extracting and matching 96 | 97 | void PrepareSignatureDB(const vector& curve_, vector >& DB, vector& DB_params) { 98 | vector curve; 99 | if (curve_.size() != 200) { 100 | ResampleCurve(curve_, curve, 200, true); 101 | }else { 102 | curve = curve_; 103 | } 104 | 105 | 106 | vector kappa; 107 | vector smooth; 108 | SimpleSmoothCurve(curve, smooth, 5.0, true); 109 | vector small; 110 | 111 | DB.clear(); DB_params.clear(); 112 | for (int len = 50; len < smooth.size() - 2; len+=5) { 113 | //iterate different curve sizes, starting at 20 points 114 | // cout << "len " << len << endl; 115 | 116 | for (int off = (smooth.size() - len); off >= 0; off-=5) { 117 | //iterate segments on A curve 118 | vector small_smooth_input(smooth.begin()+off,smooth.begin()+off+len); 119 | 120 | //resample to N points 121 | ResampleCurve(small_smooth_input, small, 200, true); 122 | 123 | //compute curvature 124 | vector small_smooth; 125 | ComputeCurveCSS(small, kappa, small_smooth, 0.66667, true); 126 | vector kappa_(kappa.begin()+1,kappa.end()-1); 127 | 128 | DB.push_back(kappa_); 129 | DB_params.push_back(Point(len,off)); 130 | } 131 | } 132 | 133 | cout << "DB size " << DB.size() << endl; 134 | } 135 | 136 | 137 | 138 | template 139 | struct CrossCorrelationDistance 140 | { 141 | typedef cvflann::True is_kdtree_distance; 142 | typedef cvflann::True is_vector_space_distance; 143 | 144 | typedef T ElementType; 145 | typedef typename Accumulator::Type ResultType; 146 | 147 | template 148 | ResultType operator()(Iterator1 x, Iterator2 y, size_t size, ResultType /*worst_dist*/ = -1) const 149 | { 150 | int i,j,n = size; 151 | ResultType mx,my,sx,sy,sxy,denom,r; 152 | 153 | /* Calculate the mean of the two series x[], y[] */ 154 | mx = 0; 155 | my = 0; 156 | for (i=0;i 184 | inline ResultType accum_dist(const U& a, const V& b, int) const 185 | { 186 | return a*b; 187 | } 188 | }; 189 | 190 | 191 | 192 | void CompareCurvesUsingFLANN(const vector& DB, 193 | const vector >& query_DB, 194 | int& a_id, 195 | int& a_subset_id, 196 | int& b_subset_id) 197 | { 198 | Mat packed(DB.size()*DB[0].rows,DB[0].cols,CV_64FC1); 199 | int rowstep = DB[0].rows; 200 | for (int i=0; i > gi(packed,cvflann::KDTreeIndexParams()); 210 | cout << "DONE\n"; 211 | Mat indices(packed_query.rows,1,CV_32SC1),dists(packed_query.rows,1,CV_64FC1); 212 | CV_PROFILE(gi.knnSearch(packed_query, indices, dists, 1, cvflann::SearchParams());) 213 | 214 | // cout << dists << endl; 215 | 216 | // dists = Mat(abs(dists)); 217 | 218 | // minMaxIdx(dists, 0, 0, &b_subset_id); 219 | vector > scores; 220 | for (int i=0; i(i); 222 | if (isnan(d)) continue; 223 | scores.push_back(make_pair(d, i)); 224 | } 225 | sort(scores.begin(),scores.end()); 226 | for (int i=0; i<20; i++) { 227 | cout << scores[i].first << " = " << scores[i].second << "(" << indices.at(scores[i].second) << ")" << endl; 228 | } 229 | b_subset_id = scores.front().second; 230 | 231 | a_id = indices.at(b_subset_id) / rowstep; 232 | a_subset_id = indices.at(b_subset_id) % rowstep; 233 | cout << "minimal "<< indices.at(b_subset_id) <<" = " << a_id << "(" << a_subset_id << ") -> " << b_subset_id << ": " << dists.at(b_subset_id) << endl; 234 | 235 | { 236 | vector a_sig(packed.cols); 237 | memcpy(&(a_sig[0]), packed.row(indices.at(b_subset_id)).data, sizeof(double)*a_sig.size()); 238 | ShowMathMLCurves(query_DB[b_subset_id], a_sig, "curvatures"); 239 | } 240 | } 241 | 242 | void CompareCurvesUsingSignatureDBMatcher(FlannBasedMatcher& matcher, 243 | const vector& typical_params, 244 | const vector >& b_DB, 245 | int& a_id, 246 | int& a_len, 247 | int& a_off, 248 | int& b_len, 249 | int& b_off, 250 | double& score 251 | ) 252 | { 253 | std::vector< DMatch > matches; 254 | { 255 | Mat_ b_DB_m = ConvertToMat(b_DB); 256 | CV_PROFILE(matcher.match( b_DB_m, matches );) 257 | } 258 | double min_dist = std::numeric_limits::max(); 259 | 260 | DMatch min_match; 261 | //-- Quick calculation of max and min distances between keypoints 262 | for( int i = matches.size()-1; i >=0 ; i-- ) 263 | { 264 | int imgidx = matches[i].imgIdx; 265 | double dist = (max(1.0,200.0 - (double)(typical_params[matches[i].queryIdx].x)) + 266 | max(1.0,200.0 - (double)(typical_params[matches[i].trainIdx].x))) + 267 | 100.0*matches[i].distance; 268 | if( dist < min_dist ) { min_dist = dist; min_match = matches[i]; } 269 | // if( dist > max_dist ) max_dist = dist; 270 | } 271 | 272 | a_id = min_match.imgIdx; 273 | // printf("-- Max dist : %f \n", max_dist ); 274 | printf("-- Min dist : %f, %d(%d,%d) -> %d(%d,%d) \n", 275 | min_dist, 276 | min_match.queryIdx, 277 | typical_params[min_match.queryIdx].x, 278 | typical_params[min_match.queryIdx].y, 279 | min_match.trainIdx, 280 | typical_params[min_match.trainIdx].x, 281 | typical_params[min_match.trainIdx].y 282 | ); 283 | 284 | // cout << Mat(a_DB[min_match.queryIdx]).t() << endl << Mat(b_DB[min_match.trainIdx]).t() << endl; 285 | 286 | a_len = typical_params[min_match.queryIdx].x; 287 | a_off = typical_params[min_match.queryIdx].y; 288 | b_len = typical_params[min_match.trainIdx].x; 289 | b_off = typical_params[min_match.trainIdx].y; 290 | } 291 | 292 | void CompareCurvesUsingSignatureDB(const vector& a_DB_params, 293 | const vector& b_DB_params, 294 | const vector >& a_DB, 295 | const vector >& b_DB, 296 | vector >& scores_to_matches 297 | ) 298 | { 299 | FlannBasedMatcher matcher; 300 | // BFMatcher matcher(NORM_L2); 301 | std::vector< DMatch > matches; 302 | { 303 | Mat_ mnt_DB_m = ConvertToMat(a_DB); 304 | Mat_ obj_DB_m = ConvertToMat(b_DB); 305 | vector obj_DB_mv(1,obj_DB_m); 306 | matcher.add(obj_DB_mv); 307 | CV_PROFILE(matcher.train();) 308 | CV_PROFILE(matcher.match( mnt_DB_m, matches );) 309 | } 310 | 311 | DMatch min_match; 312 | 313 | vector > scores; 314 | for( int i = matches.size()-1; i >=0 ; i-- ) 315 | { 316 | double d = //(max(1.0,200.0 - (double)(a_DB_params[matches[i].queryIdx].x)) + 317 | // max(1.0,200.0 - (double)(b_DB_params[matches[i].trainIdx].x))) + 318 | 10000.0 * matches[i].distance; 319 | if (isnan(d)) continue; 320 | scores.push_back(make_pair(d, i)); 321 | } 322 | sort(scores.begin(),scores.end()); 323 | 324 | scores_to_matches.clear(); 325 | for (int i=0; i<200; i++) { 326 | // cout << scores[i].first << " = " << scores[i].second << endl; 327 | scores_to_matches.push_back(make_pair(scores[i].first, matches[scores[i].second])); 328 | } 329 | min_match = matches[scores.front().second]; 330 | // double min_dist = scores.front().first; 331 | 332 | // printf("-- Max dist : %f \n", max_dist ); 333 | // printf("-- Min dist : %f, %d(%d,%d) -> %d(%d,%d) \n", 334 | // min_dist, 335 | // min_match.queryIdx, 336 | // a_DB_params[min_match.queryIdx].x, 337 | // a_DB_params[min_match.queryIdx].y, 338 | // min_match.trainIdx, 339 | // b_DB_params[min_match.trainIdx].x, 340 | // b_DB_params[min_match.trainIdx].y 341 | // ); 342 | // 343 | // cout << Mat(a_DB[min_match.queryIdx]).t() << endl << Mat(b_DB[min_match.trainIdx]).t() << endl; 344 | // { 345 | // vector a_sig,b_sig; 346 | // a_sig = a_DB[matches[scores.front().second].queryIdx], 347 | // b_sig = b_DB[matches[scores.front().second].trainIdx]; 348 | // ShowMathMLCurves(a_sig, b_sig, "curvatures0"); 349 | // 350 | // a_sig = a_DB[matches[scores[1].second].queryIdx]; 351 | // b_sig = b_DB[matches[scores[1].second].trainIdx]; 352 | // ShowMathMLCurves(a_sig, b_sig, "curvatures1"); 353 | // 354 | // a_sig = a_DB[matches[scores[2].second].queryIdx]; 355 | // b_sig = b_DB[matches[scores[2].second].trainIdx]; 356 | // ShowMathMLCurves(a_sig, b_sig, "curvatures2"); 357 | // 358 | // a_sig = a_DB[matches[scores[3].second].queryIdx]; 359 | // b_sig = b_DB[matches[scores[3].second].trainIdx]; 360 | // ShowMathMLCurves(a_sig, b_sig, "curvatures3"); 361 | // 362 | // } 363 | } 364 | 365 | void CompareCurvesUsingSignatureDB(const vector& a, 366 | const vector& b, 367 | int& a_len, 368 | int& a_off, 369 | int& b_len, 370 | int& b_off, 371 | double& score 372 | ) 373 | { 374 | vector a_DB_params,b_DB_params; 375 | vector > a_DB,b_DB; 376 | PrepareSignatureDB(a, a_DB, a_DB_params); 377 | PrepareSignatureDB(b, b_DB, b_DB_params); 378 | 379 | vector > scores_to_matches; 380 | CompareCurvesUsingSignatureDB(a_DB_params,b_DB_params,a_DB,b_DB,scores_to_matches); 381 | 382 | //re-rank results by RMSE measure after recovering rigid transformation 383 | for (int i=0; i a_subset(a.begin() + _a_off, a.begin() + _a_off + _a_len); 390 | vector b_subset(b.begin() + _b_off, b.begin() + _b_off + _b_len); 391 | 392 | ResampleCurve(a_subset, a_subset, 200, true); 393 | ResampleCurve(b_subset, b_subset, 200, true); 394 | 395 | Mat trans = Find2DRigidTransform(a_subset, b_subset); 396 | // cout << trans << endl; 397 | vector a_trans; 398 | cv::transform(a_subset,a_trans,trans); 399 | 400 | double rmse = 0; 401 | for (int pt=0; pt ("<<_b_len<<","<<_b_off<<") RMSE: " << rmse << endl; 407 | scores_to_matches[i].first = rmse; 408 | } 409 | sort(scores_to_matches.begin(), scores_to_matches.end()); 410 | 411 | { 412 | //Show curvatures 413 | vector a_sig,b_sig; 414 | a_sig = a_DB[scores_to_matches.front().second.queryIdx], 415 | b_sig = b_DB[scores_to_matches.front().second.trainIdx]; 416 | ShowMathMLCurves(a_sig, b_sig, "curvatures0"); 417 | } 418 | 419 | a_len = a_DB_params[scores_to_matches.front().second.queryIdx].x; 420 | a_off = a_DB_params[scores_to_matches.front().second.queryIdx].y; 421 | b_len = b_DB_params[scores_to_matches.front().second.trainIdx].x; 422 | b_off = b_DB_params[scores_to_matches.front().second.trainIdx].y; 423 | score = scores_to_matches.front().first; 424 | 425 | cout << "("< ("<& a, const Mat_& b) { 432 | double max_cc = 0.0; 433 | int max_len,max_off,max_y,max_offb,max_yb; 434 | 435 | for (int len = 20; len < a.cols; len++) { 436 | //iterate different curve sizes, starting at 20 points 437 | cout << "len " << len << endl; 438 | 439 | for (int y = 0; y < a.rows; y++) { 440 | //iterate sigma values on A (rows of A signature) 441 | 442 | for (int yb = 0; yb templt; a(Range(y,y+1),Range(off,off+len)).copyTo(templt); 448 | assert(templt.size() == len); 449 | 450 | for (int offb = 0; offb < (b.cols - len); offb++) { 451 | vector bv; b(Range(yb,yb+1),Range(offb,offb+len)).copyTo(bv); 452 | 453 | double cc = CalcCrossCorrelation(templt, bv) * (double)len; 454 | if (cc > max_cc) { 455 | max_cc = cc; 456 | max_y = y; max_off = off; max_len = len; max_offb = offb; max_yb = yb; 457 | } 458 | } 459 | } 460 | } 461 | } 462 | } 463 | } 464 | 465 | #define STRINGIFY(A) #A 466 | 467 | #ifndef WITHOUT_OPENCL 468 | void CompareCurvesGPU(const vector& obj_curve_, 469 | const vector& upperEnvelope_, 470 | double& a_sigma, 471 | double& b_sigma, 472 | int& a_offset, 473 | int& b_offset, 474 | int& len 475 | ) 476 | { 477 | int resample_size = 200; 478 | 479 | vector obj_curve,a; 480 | if(upperEnvelope_.size()!=resample_size) ResampleCurve(upperEnvelope_,a,resample_size,true); 481 | else a.insert(a.begin(),upperEnvelope_.begin(),upperEnvelope_.end()); 482 | Mat_ mountain_hks; 483 | CurveSignature(a, mountain_hks,5); 484 | double minval,maxval; 485 | minMaxLoc(mountain_hks, &minval, &maxval); 486 | imshow_<800,200>("b HKS",(mountain_hks-minval)/(maxval-minval)); 487 | Mat_ mountain_hks_32f; 488 | mountain_hks.convertTo(mountain_hks_32f, CV_32F); 489 | 490 | for (double scale = 1.0; scale >= 0.5; scale -= 0.1) { 491 | int scaled_resample_size = cvRound((double)resample_size * scale); 492 | if(obj_curve_.size()!=scaled_resample_size) ResampleCurve(obj_curve_,obj_curve,scaled_resample_size,true); 493 | else obj_curve.insert(obj_curve.begin(),obj_curve_.begin(),obj_curve_.end()); 494 | 495 | Mat_ camel_hks; 496 | CurveSignature(obj_curve,camel_hks,5); 497 | 498 | minMaxLoc(camel_hks, &minval, &maxval); 499 | imshow_<800,200>("a HKS",(camel_hks-minval)/(maxval-minval)); 500 | 501 | Mat_ camel_hks_32f; 502 | camel_hks.convertTo(camel_hks_32f, CV_32F); 503 | // vector camel_hks_sig1; for(int i=0;i mountain_hks_sig1; for(int i=0;i obj_hks_len; camel_hks(Range(a_row,a_row+1),Range(a_offset,a_offset+len)).copyTo(obj_hks_len); 517 | vector mnt_hks_len; mountain_hks(Range(b_row,b_row+1),Range(b_offset,b_offset+len)).copyTo(mnt_hks_len); 518 | vector count_; for(int x=0;x obj_hks_len_p2d; PolyLineMerge(obj_hks_len_p2d, count_, obj_hks_len); 520 | vector mnt_hks_len_p2d; PolyLineMerge(mnt_hks_len_p2d, count_, mnt_hks_len); 521 | ShowMathMLCurves(obj_hks_len_p2d, 522 | mnt_hks_len_p2d, 523 | "Curvature"); 524 | } 525 | } 526 | } 527 | 528 | void CompareSignaturesGPU(const Mat_& a, const Mat_& b, 529 | double& a_sigma, 530 | double& b_sigma, 531 | int& a_offset, 532 | int& b_offset, 533 | int& len 534 | ) { 535 | Mat_ toA = a; //(Range(0,15),Range::all()); 536 | Mat_ toB = b; //(Range(0,15),Range::all()); 537 | CLSignatureMatching clsigmatch; 538 | //load and build our CL program from the file 539 | #include "SignatureMatching.cl" //const char* kernel_source is defined in here 540 | CV_PROFILE(clsigmatch.loadProgram(kernel_source);) 541 | 542 | clsigmatch.setInput(toA,toB); 543 | 544 | //initialize the kernel and send data from the CPU to the GPU 545 | CV_PROFILE(clsigmatch.popCorn();) 546 | 547 | //execute the kernel 548 | CV_PROFILE(clsigmatch.runKernel();) 549 | 550 | imshow_<500,500>("output",clsigmatch.output); 551 | // vector outputv; split(clsigmatch.output, outputv); 552 | // cout << outputv[0] << endl; 553 | 554 | // Mat_ myoutput(outputv[0].size()); 555 | // for (int i=0; i max_score) { 567 | // max_score = score; 568 | // max_cc = cc; 569 | // max_len = len; 570 | // } 571 | // } 572 | // } 573 | // } 574 | // myoutput(i,j) = max_cc; 575 | // } 576 | // } 577 | // cout << myoutput << endl; 578 | // cout << "mean error " << mean(myoutput - outputv[0].t())[0] << endl; 579 | // waitKey(); 580 | 581 | vector ccv; 582 | split(clsigmatch.output, ccv); 583 | Point maxLoc; 584 | minMaxLoc(ccv[0], 0, 0, 0, &maxLoc); 585 | 586 | cout << "highest CC found at " << maxLoc << endl; 587 | cout << "CC = " << ccv[0].at(maxLoc.y,maxLoc.x) << endl; 588 | cout << "off A = " << ccv[1].at(maxLoc.y,maxLoc.x) << endl; 589 | cout << "off B = " << ccv[2].at(maxLoc.y,maxLoc.x) << endl; 590 | cout << "Len = " << ccv[3].at(maxLoc.y,maxLoc.x) << endl; 591 | 592 | a_sigma = maxLoc.x / 10.0; 593 | b_sigma = maxLoc.y / 10.0; 594 | a_offset = ccv[1].at(maxLoc.y,maxLoc.x); 595 | b_offset = ccv[2].at(maxLoc.y,maxLoc.x); 596 | len = ccv[3].at(maxLoc.y,maxLoc.x); 597 | } 598 | 599 | 600 | #endif -------------------------------------------------------------------------------- /CMake/cotire.cmake: -------------------------------------------------------------------------------- 1 | # - cotire (compile time reducer) 2 | # 3 | # See the cotire manual for usage hints. 4 | # 5 | #============================================================================= 6 | # Copyright 2012 Sascha Kratky 7 | # 8 | # Permission is hereby granted, free of charge, to any person 9 | # obtaining a copy of this software and associated documentation 10 | # files (the "Software"), to deal in the Software without 11 | # restriction, including without limitation the rights to use, 12 | # copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | # copies of the Software, and to permit persons to whom the 14 | # Software is furnished to do so, subject to the following 15 | # conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be 18 | # included in all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | # OTHER DEALINGS IN THE SOFTWARE. 28 | #============================================================================= 29 | 30 | if(__COTIRE_INCLUDED) 31 | return() 32 | endif() 33 | set(__COTIRE_INCLUDED TRUE) 34 | 35 | # call cmake_minimum_required, but prevent modification of the CMake policy stack in include mode 36 | if (NOT CMAKE_SCRIPT_MODE_FILE) 37 | cmake_policy(PUSH) 38 | endif() 39 | # we need the CMake variables CMAKE_SCRIPT_MODE_FILE and CMAKE_ARGV available since 2.8.5 40 | # we need APPEND_STRING option for set_property available since 2.8.6 41 | cmake_minimum_required(VERSION 2.8.6) 42 | if (NOT CMAKE_SCRIPT_MODE_FILE) 43 | cmake_policy(POP) 44 | endif() 45 | 46 | set (COTIRE_CMAKE_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}") 47 | set (COTIRE_CMAKE_MODULE_VERSION "1.2.0") 48 | 49 | include(CMakeParseArguments) 50 | 51 | function (cotire_determine_compiler_version _language _versionPrefix) 52 | if (NOT ${_versionPrefix}_VERSION) 53 | if (MSVC) 54 | # use CMake's predefined version variable for MSVC, if available 55 | if (DEFINED MSVC_VERSION) 56 | set (${_versionPrefix}_VERSION "${MSVC_VERSION}") 57 | else() 58 | # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared 59 | unset (ENV{VS_UNICODE_OUTPUT}) 60 | string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1) 61 | execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1} 62 | ERROR_VARIABLE _versionLine OUTPUT_QUIET TIMEOUT 10) 63 | string (REGEX REPLACE ".*Version *([0-9]+(\\.[0-9]+)*).*" "\\1" 64 | ${_versionPrefix}_VERSION "${_versionLine}") 65 | endif() 66 | else() 67 | # use CMake's predefined compiler version variable (available since CMake 2.8.8) 68 | if (DEFINED CMAKE_${_language}_COMPILER_VERSION) 69 | set (${_versionPrefix}_VERSION "${CMAKE_${_language}_COMPILER_VERSION}") 70 | else() 71 | # assume GCC like command line interface 72 | string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1) 73 | execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1} "-dumpversion" 74 | OUTPUT_VARIABLE ${_versionPrefix}_VERSION 75 | OUTPUT_STRIP_TRAILING_WHITESPACE TIMEOUT 10) 76 | endif() 77 | endif() 78 | if (${_versionPrefix}_VERSION) 79 | set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" CACHE INTERNAL "${_language} compiler version") 80 | endif() 81 | set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" PARENT_SCOPE) 82 | if (COTIRE_DEBUG) 83 | message (STATUS "${CMAKE_${_language}_COMPILER} version ${${_versionPrefix}_VERSION}") 84 | endif() 85 | endif() 86 | endfunction() 87 | 88 | function (cotire_get_source_file_extension _sourceFile _extVar) 89 | # get_filename_component returns extension from first occurrence of . in file name 90 | # this function computes the extension from last occurrence of . in file name 91 | string (FIND "${_sourceFile}" "." _index REVERSE) 92 | if (_index GREATER -1) 93 | math (EXPR _index "${_index} + 1") 94 | string (SUBSTRING "${_sourceFile}" ${_index} -1 _sourceExt) 95 | else() 96 | set (_sourceExt "") 97 | endif() 98 | set (${_extVar} "${_sourceExt}" PARENT_SCOPE) 99 | endfunction() 100 | 101 | macro (cotire_check_is_path_relative_to _path _isRelativeVar) 102 | set (${_isRelativeVar} FALSE) 103 | if (IS_ABSOLUTE "${_path}") 104 | foreach (_dir ${ARGN}) 105 | file (RELATIVE_PATH _relPath "${_dir}" "${_path}") 106 | if (NOT _relPath OR (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.")) 107 | set (${_isRelativeVar} TRUE) 108 | break() 109 | endif() 110 | endforeach() 111 | endif() 112 | endmacro() 113 | 114 | function (cotire_filter_language_source_files _language _sourceFilesVar _excludedSourceFilesVar _cotiredSourceFilesVar) 115 | set (_sourceFiles "") 116 | set (_excludedSourceFiles "") 117 | set (_cotiredSourceFiles "") 118 | if (CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) 119 | set (_languageExtensions "${CMAKE_${_language}_SOURCE_FILE_EXTENSIONS}") 120 | else() 121 | set (_languageExtensions "") 122 | endif() 123 | if (CMAKE_${_language}_IGNORE_EXTENSIONS) 124 | set (_ignoreExtensions "${CMAKE_${_language}_IGNORE_EXTENSIONS}") 125 | else() 126 | set (_ignoreExtensions "") 127 | endif() 128 | if (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS) 129 | set (_excludeExtensions "${COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS}") 130 | else() 131 | set (_excludeExtensions "") 132 | endif() 133 | if (COTIRE_DEBUG) 134 | message (STATUS "${_language} source file extensions: ${_languageExtensions}") 135 | message (STATUS "${_language} ignore extensions: ${_ignoreExtensions}") 136 | message (STATUS "${_language} exclude extensions: ${_excludeExtensions}") 137 | endif() 138 | foreach (_sourceFile ${ARGN}) 139 | get_source_file_property(_sourceIsHeaderOnly "${_sourceFile}" HEADER_FILE_ONLY) 140 | get_source_file_property(_sourceIsExternal "${_sourceFile}" EXTERNAL_OBJECT) 141 | get_source_file_property(_sourceIsSymbolic "${_sourceFile}" SYMBOLIC) 142 | get_source_file_property(_sourceLanguage "${_sourceFile}" LANGUAGE) 143 | set (_sourceIsFiltered FALSE) 144 | if (NOT _sourceIsHeaderOnly AND NOT _sourceIsExternal AND NOT _sourceIsSymbolic) 145 | cotire_get_source_file_extension("${_sourceFile}" _sourceExt) 146 | if (_sourceExt) 147 | list (FIND _ignoreExtensions "${_sourceExt}" _ignoreIndex) 148 | if (_ignoreIndex LESS 0) 149 | list (FIND _excludeExtensions "${_sourceExt}" _excludeIndex) 150 | if (_excludeIndex GREATER -1) 151 | list (APPEND _excludedSourceFiles "${_sourceFile}") 152 | else() 153 | list (FIND _languageExtensions "${_sourceExt}" _sourceIndex) 154 | if (_sourceIndex GREATER -1) 155 | set (_sourceIsFiltered TRUE) 156 | elseif ("${_sourceLanguage}" STREQUAL "${_language}") 157 | # add to excluded sources, if file is not ignored and has correct language without having the correct extension 158 | list (APPEND _excludedSourceFiles "${_sourceFile}") 159 | endif() 160 | endif() 161 | endif() 162 | endif() 163 | endif() 164 | if (COTIRE_DEBUG) 165 | message (STATUS "${_sourceFile} filtered=${_sourceIsFiltered} language=${_sourceLanguage} header=${_sourceIsHeaderOnly}") 166 | endif() 167 | if (_sourceIsFiltered) 168 | get_source_file_property(_sourceIsExcluded "${_sourceFile}" COTIRE_EXCLUDED) 169 | get_source_file_property(_sourceIsCotired "${_sourceFile}" COTIRE_TARGET) 170 | get_source_file_property(_sourceCompileFlags "${_sourceFile}" COMPILE_FLAGS) 171 | if (COTIRE_DEBUG) 172 | message (STATUS "${_sourceFile} excluded=${_sourceIsExcluded} cotired=${_sourceIsCotired}") 173 | endif() 174 | if (_sourceIsCotired) 175 | list (APPEND _cotiredSourceFiles "${_sourceFile}") 176 | elseif (_sourceIsExcluded OR _sourceCompileFlags) 177 | list (APPEND _excludedSourceFiles "${_sourceFile}") 178 | else() 179 | list (APPEND _sourceFiles "${_sourceFile}") 180 | endif() 181 | endif() 182 | endforeach() 183 | if (COTIRE_DEBUG) 184 | message (STATUS "All: ${ARGN}") 185 | message (STATUS "${_language}: ${_sourceFiles}") 186 | message (STATUS "Excluded: ${_excludedSourceFiles}") 187 | message (STATUS "Cotired: ${_cotiredSourceFiles}") 188 | endif() 189 | set (${_sourceFilesVar} ${_sourceFiles} PARENT_SCOPE) 190 | set (${_excludedSourceFilesVar} ${_excludedSourceFiles} PARENT_SCOPE) 191 | set (${_cotiredSourceFilesVar} ${_cotiredSourceFiles} PARENT_SCOPE) 192 | endfunction() 193 | 194 | function (cotire_get_objects_with_property_on _filteredObjectsVar _property _type) 195 | set (_filteredObjects "") 196 | foreach (_object ${ARGN}) 197 | get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) 198 | if (_isSet) 199 | get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) 200 | if (_propertyValue) 201 | list (APPEND _filteredObjects "${_object}") 202 | endif() 203 | endif() 204 | endforeach() 205 | set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) 206 | endfunction() 207 | 208 | function (cotire_get_objects_with_property_off _filteredObjectsVar _property _type) 209 | set (_filteredObjects "") 210 | foreach (_object ${ARGN}) 211 | get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) 212 | if (_isSet) 213 | get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) 214 | if (NOT _propertyValue) 215 | list (APPEND _filteredObjects "${_object}") 216 | endif() 217 | endif() 218 | endforeach() 219 | set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) 220 | endfunction() 221 | 222 | function (cotire_get_source_file_property_values _valuesVar _property) 223 | set (_values "") 224 | foreach (_sourceFile ${ARGN}) 225 | get_source_file_property(_propertyValue "${_sourceFile}" ${_property}) 226 | if (_propertyValue) 227 | list (APPEND _values "${_propertyValue}") 228 | endif() 229 | endforeach() 230 | set (${_valuesVar} ${_values} PARENT_SCOPE) 231 | endfunction() 232 | 233 | function (cotrie_resolve_config_properites _configurations _propertiesVar) 234 | set (_properties "") 235 | foreach (_property ${ARGN}) 236 | if ("${_property}" MATCHES "") 237 | foreach (_config ${_configurations}) 238 | string (TOUPPER "${_config}" _upperConfig) 239 | string (REPLACE "" "${_upperConfig}" _configProperty "${_property}") 240 | list (APPEND _properties ${_configProperty}) 241 | endforeach() 242 | else() 243 | list (APPEND _properties ${_property}) 244 | endif() 245 | endforeach() 246 | set (${_propertiesVar} ${_properties} PARENT_SCOPE) 247 | endfunction() 248 | 249 | function (cotrie_copy_set_properites _configurations _type _source _target) 250 | cotrie_resolve_config_properites("${_configurations}" _properties ${ARGN}) 251 | foreach (_property ${_properties}) 252 | get_property(_isSet ${_type} ${_source} PROPERTY ${_property} SET) 253 | if (_isSet) 254 | get_property(_propertyValue ${_type} ${_source} PROPERTY ${_property}) 255 | set_property(${_type} ${_target} PROPERTY ${_property} "${_propertyValue}") 256 | endif() 257 | endforeach() 258 | endfunction() 259 | 260 | function (cotire_filter_compile_flags _flagFilter _matchedOptionsVar _unmatchedOptionsVar) 261 | if (MSVC) 262 | set (_flagPrefix "[/-]") 263 | else() 264 | set (_flagPrefix "--?") 265 | endif() 266 | set (_optionFlag "") 267 | set (_matchedOptions "") 268 | set (_unmatchedOptions "") 269 | foreach (_compileFlag ${ARGN}) 270 | if (_compileFlag) 271 | if (_optionFlag AND NOT "${_compileFlag}" MATCHES "^${_flagPrefix}") 272 | # option with separate argument 273 | list (APPEND _matchedOptions "${_compileFlag}") 274 | set (_optionFlag "") 275 | elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})$") 276 | # remember option 277 | set (_optionFlag "${CMAKE_MATCH_2}") 278 | elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})(.+)$") 279 | # option with joined argument 280 | list (APPEND _matchedOptions "${CMAKE_MATCH_3}") 281 | set (_optionFlag "") 282 | else() 283 | # flush remembered option 284 | if (_optionFlag) 285 | list (APPEND _matchedOptions "${_optionFlag}") 286 | set (_optionFlag "") 287 | endif() 288 | # add to unfiltered options 289 | list (APPEND _unmatchedOptions "${_compileFlag}") 290 | endif() 291 | endif() 292 | endforeach() 293 | if (_optionFlag) 294 | list (APPEND _matchedOptions "${_optionFlag}") 295 | endif() 296 | if (COTIRE_DEBUG) 297 | message (STATUS "Filter ${_flagFilter}") 298 | if (_matchedOptions) 299 | message (STATUS "Matched ${_matchedOptions}") 300 | endif() 301 | if (_unmatchedOptions) 302 | message (STATUS "Unmatched ${_unmatchedOptions}") 303 | endif() 304 | endif() 305 | set (${_matchedOptionsVar} ${_matchedOptions} PARENT_SCOPE) 306 | set (${_unmatchedOptionsVar} ${_unmatchedOptions} PARENT_SCOPE) 307 | endfunction() 308 | 309 | function (cotire_get_target_compile_flags _config _language _directory _target _flagsVar) 310 | string (TOUPPER "${_config}" _upperConfig) 311 | # collect options from CMake language variables 312 | set (_compileFlags "") 313 | if (CMAKE_${_language}_FLAGS) 314 | set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS}") 315 | endif() 316 | if (CMAKE_${_language}_FLAGS_${_upperConfig}) 317 | set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS_${_upperConfig}}") 318 | endif() 319 | if (_target) 320 | # add option from CMake target type variable 321 | get_target_property(_targetType ${_target} TYPE) 322 | if (CMAKE_VERSION VERSION_LESS "2.8.9") 323 | set (_PIC_Policy "OLD") 324 | else() 325 | # handle POSITION_INDEPENDENT_CODE property introduced with CMake 2.8.9 if policy CMP0018 is turned on 326 | cmake_policy(GET CMP0018 _PIC_Policy) 327 | endif() 328 | if (COTIRE_DEBUG) 329 | message(STATUS "CMP0018=${_PIC_Policy}, CMAKE_POLICY_DEFAULT_CMP0018=${CMAKE_POLICY_DEFAULT_CMP0018}") 330 | endif() 331 | if (_PIC_Policy STREQUAL "NEW") 332 | # NEW behavior: honor the POSITION_INDEPENDENT_CODE target property 333 | get_target_property(_targetPIC ${_target} POSITION_INDEPENDENT_CODE) 334 | if (_targetPIC) 335 | if (_targetType STREQUAL "EXECUTABLE") 336 | if (CMAKE_${_targetType}_${_language}_FLAGS) 337 | set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIE}") 338 | else() 339 | set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIC}") 340 | endif() 341 | endif() 342 | endif() 343 | else() 344 | # OLD behavior or policy not set: use the value of CMAKE_SHARED_LIBRARY__FLAGS 345 | if (_targetType STREQUAL "MODULE_LIBRARY") 346 | # flags variable for module library uses different name SHARED_MODULE 347 | # (e.g., CMAKE_SHARED_MODULE_C_FLAGS) 348 | set (_targetType SHARED_MODULE) 349 | endif() 350 | if (CMAKE_${_targetType}_${_language}_FLAGS) 351 | set (_compileFlags "${_compileFlags} ${CMAKE_${_targetType}_${_language}_FLAGS}") 352 | endif() 353 | endif() 354 | endif() 355 | if (_directory) 356 | # add_definitions may have been used to add flags to the compiler command 357 | get_directory_property(_dirDefinitions DIRECTORY "${_directory}" DEFINITIONS) 358 | if (_dirDefinitions) 359 | set (_compileFlags "${_compileFlags} ${_dirDefinitions}") 360 | endif() 361 | endif() 362 | if (_target) 363 | # add target compile options 364 | get_target_property(_targetflags ${_target} COMPILE_FLAGS) 365 | if (_targetflags) 366 | set (_compileFlags "${_compileFlags} ${_targetflags}") 367 | endif() 368 | endif() 369 | if (UNIX) 370 | separate_arguments(_compileFlags UNIX_COMMAND "${_compileFlags}") 371 | elseif(WIN32) 372 | separate_arguments(_compileFlags WINDOWS_COMMAND "${_compileFlags}") 373 | else() 374 | separate_arguments(_compileFlags) 375 | endif() 376 | # platform specific flags 377 | if (APPLE) 378 | get_target_property(_architectures ${_target} OSX_ARCHITECTURES_${_upperConfig}) 379 | if (NOT _architectures) 380 | get_target_property(_architectures ${_target} OSX_ARCHITECTURES) 381 | endif() 382 | foreach (_arch ${_architectures}) 383 | list (APPEND _compileFlags "-arch" "${_arch}") 384 | endforeach() 385 | if (CMAKE_OSX_SYSROOT AND CMAKE_OSX_SYSROOT_DEFAULT AND CMAKE_${_language}_HAS_ISYSROOT) 386 | if (NOT "${CMAKE_OSX_SYSROOT}" STREQUAL "${CMAKE_OSX_SYSROOT_DEFAULT}") 387 | list (APPEND _compileFlags "-isysroot" "${CMAKE_OSX_SYSROOT}") 388 | endif() 389 | endif() 390 | if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG) 391 | list (APPEND _compileFlags "${CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}") 392 | endif() 393 | endif() 394 | if (COTIRE_DEBUG AND _compileFlags) 395 | message (STATUS "Target ${_target} compile flags ${_compileFlags}") 396 | endif() 397 | set (${_flagsVar} ${_compileFlags} PARENT_SCOPE) 398 | endfunction() 399 | 400 | function (cotire_get_target_include_directories _config _language _targetSourceDir _targetBinaryDir _target _includeDirsVar) 401 | set (_includeDirs "") 402 | # default include dirs 403 | if (CMAKE_INCLUDE_CURRENT_DIR) 404 | list (APPEND _includeDirs "${_targetBinaryDir}") 405 | list (APPEND _includeDirs "${_targetSourceDir}") 406 | endif() 407 | # parse additional include directories from target compile flags 408 | cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags) 409 | cotire_filter_compile_flags("I" _dirs _ignore ${_targetFlags}) 410 | if (_dirs) 411 | list (APPEND _includeDirs ${_dirs}) 412 | endif() 413 | # target include directories 414 | get_directory_property(_dirs DIRECTORY "${_targetSourceDir}" INCLUDE_DIRECTORIES) 415 | if (_target) 416 | get_target_property(_targetDirs ${_target} INCLUDE_DIRECTORIES) 417 | if (_targetDirs) 418 | list (APPEND _dirs ${_targetDirs}) 419 | list (REMOVE_DUPLICATES _dirs) 420 | endif() 421 | endif() 422 | list (LENGTH _includeDirs _projectInsertIndex) 423 | foreach (_dir ${_dirs}) 424 | if (CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE) 425 | cotire_check_is_path_relative_to("${_dir}" _isRelative "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}") 426 | if (_isRelative) 427 | list (LENGTH _includeDirs _len) 428 | if (_len EQUAL _projectInsertIndex) 429 | list (APPEND _includeDirs "${_dir}") 430 | else() 431 | list (INSERT _includeDirs _projectInsertIndex "${_dir}") 432 | endif() 433 | math (EXPR _projectInsertIndex "${_projectInsertIndex} + 1") 434 | else() 435 | list (APPEND _includeDirs "${_dir}") 436 | endif() 437 | else() 438 | list (APPEND _includeDirs "${_dir}") 439 | endif() 440 | endforeach() 441 | list (REMOVE_DUPLICATES _includeDirs) 442 | if (CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES) 443 | list (REMOVE_ITEM _includeDirs ${CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES}) 444 | endif() 445 | if (COTIRE_DEBUG AND _includeDirs) 446 | message (STATUS "Target ${_target} include dirs ${_includeDirs}") 447 | endif() 448 | set (${_includeDirsVar} ${_includeDirs} PARENT_SCOPE) 449 | endfunction() 450 | 451 | macro (cotire_make_C_identifier _identifierVar _str) 452 | # mimic CMake SystemTools::MakeCindentifier behavior 453 | if ("${_str}" MATCHES "^[0-9].+$") 454 | set (_str "_${str}") 455 | endif() 456 | string (REGEX REPLACE "[^a-zA-Z0-9]" "_" ${_identifierVar} "${_str}") 457 | endmacro() 458 | 459 | function (cotire_get_target_export_symbol _target _exportSymbolVar) 460 | set (_exportSymbol "") 461 | get_target_property(_targetType ${_target} TYPE) 462 | get_target_property(_enableExports ${_target} ENABLE_EXPORTS) 463 | if (_targetType MATCHES "(SHARED|MODULE)_LIBRARY" OR 464 | (_targetType STREQUAL "EXECUTABLE" AND _enableExports)) 465 | get_target_property(_exportSymbol ${_target} DEFINE_SYMBOL) 466 | if (NOT _exportSymbol) 467 | set (_exportSymbol "${_target}_EXPORTS") 468 | endif() 469 | cotire_make_C_identifier(_exportSymbol "${_exportSymbol}") 470 | endif() 471 | set (${_exportSymbolVar} ${_exportSymbol} PARENT_SCOPE) 472 | endfunction() 473 | 474 | function (cotire_get_target_compile_definitions _config _language _directory _target _definitionsVar) 475 | string (TOUPPER "${_config}" _upperConfig) 476 | set (_configDefinitions "") 477 | # CMAKE_INTDIR for multi-configuration build systems 478 | if (NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".") 479 | list (APPEND _configDefinitions "CMAKE_INTDIR=\"${_config}\"") 480 | endif() 481 | # target export define symbol 482 | cotire_get_target_export_symbol("${_target}" _defineSymbol) 483 | if (_defineSymbol) 484 | list (APPEND _configDefinitions "${_defineSymbol}") 485 | endif() 486 | # directory compile definitions 487 | get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS) 488 | if (_definitions) 489 | list (APPEND _configDefinitions ${_definitions}) 490 | endif() 491 | get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS_${_upperConfig}) 492 | if (_definitions) 493 | list (APPEND _configDefinitions ${_definitions}) 494 | endif() 495 | # target compile definitions 496 | get_target_property(_definitions ${_target} COMPILE_DEFINITIONS) 497 | if (_definitions) 498 | list (APPEND _configDefinitions ${_definitions}) 499 | endif() 500 | get_target_property(_definitions ${_target} COMPILE_DEFINITIONS_${_upperConfig}) 501 | if (_definitions) 502 | list (APPEND _configDefinitions ${_definitions}) 503 | endif() 504 | # parse additional compile definitions from target compile flags 505 | # and don't look at directory compile definitions, which we already handled 506 | cotire_get_target_compile_flags("${_config}" "${_language}" "" "${_target}" _targetFlags) 507 | cotire_filter_compile_flags("D" _definitions _ignore ${_targetFlags}) 508 | if (_definitions) 509 | list (APPEND _configDefinitions ${_definitions}) 510 | endif() 511 | list (REMOVE_DUPLICATES _configDefinitions) 512 | if (COTIRE_DEBUG AND _configDefinitions) 513 | message (STATUS "Target ${_target} compile definitions ${_configDefinitions}") 514 | endif() 515 | set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) 516 | endfunction() 517 | 518 | function (cotire_get_target_compiler_flags _config _language _directory _target _compilerFlagsVar) 519 | # parse target compile flags omitting compile definitions and include directives 520 | cotire_get_target_compile_flags("${_config}" "${_language}" "${_directory}" "${_target}" _targetFlags) 521 | cotire_filter_compile_flags("[ID]" _ignore _compilerFlags ${_targetFlags}) 522 | if (COTIRE_DEBUG AND _compileFlags) 523 | message (STATUS "Target ${_target} compiler flags ${_compileFlags}") 524 | endif() 525 | set (${_compilerFlagsVar} ${_compilerFlags} PARENT_SCOPE) 526 | endfunction() 527 | 528 | function (cotire_add_sys_root_paths _pathsVar) 529 | if (APPLE) 530 | if (CMAKE_OSX_SYSROOT AND CMAKE_${_language}_HAS_ISYSROOT) 531 | foreach (_path IN LISTS ${_pathsVar}) 532 | if (IS_ABSOLUTE "${_path}") 533 | get_filename_component(_path "${CMAKE_OSX_SYSROOT}/${_path}" ABSOLUTE) 534 | if (EXISTS "${_path}") 535 | list (APPEND ${_pathsVar} "${_path}") 536 | endif() 537 | endif() 538 | endforeach() 539 | endif() 540 | endif() 541 | set (${_pathsVar} ${${_pathsVar}} PARENT_SCOPE) 542 | if (COTIRE_DEBUG) 543 | message (STATUS "${_pathsVar}=${${_pathsVar}}") 544 | endif() 545 | endfunction() 546 | 547 | function (cotire_get_source_extra_properties _sourceFile _pattern _resultVar) 548 | set (_extraProperties ${ARGN}) 549 | set (_result "") 550 | if (_extraProperties) 551 | list (FIND _extraProperties "${_sourceFile}" _index) 552 | if (_index GREATER -1) 553 | math (EXPR _index "${_index} + 1") 554 | list (LENGTH _extraProperties _len) 555 | math (EXPR _len "${_len} - 1") 556 | foreach (_index RANGE ${_index} ${_len}) 557 | list (GET _extraProperties ${_index} _value) 558 | if ("${_value}" MATCHES "${_pattern}") 559 | list (APPEND _result "${_value}") 560 | else() 561 | break() 562 | endif() 563 | endforeach() 564 | endif() 565 | endif() 566 | set (${_resultVar} ${_result} PARENT_SCOPE) 567 | endfunction() 568 | 569 | function (cotire_get_source_compile_definitions _config _language _sourceFile _definitionsVar) 570 | set (_compileDefinitions "") 571 | if (NOT CMAKE_SCRIPT_MODE_FILE) 572 | string (TOUPPER "${_config}" _upperConfig) 573 | get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS) 574 | if (_definitions) 575 | list (APPEND _compileDefinitions ${_definitions}) 576 | endif() 577 | get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS_${_upperConfig}) 578 | if (_definitions) 579 | list (APPEND _compileDefinitions ${_definitions}) 580 | endif() 581 | endif() 582 | cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+(=.*)?$" _definitions ${ARGN}) 583 | if (_definitions) 584 | list (APPEND _compileDefinitions ${_definitions}) 585 | endif() 586 | if (COTIRE_DEBUG AND _compileDefinitions) 587 | message (STATUS "Source ${_sourceFile} compile definitions ${_compileDefinitions}") 588 | endif() 589 | set (${_definitionsVar} ${_compileDefinitions} PARENT_SCOPE) 590 | endfunction() 591 | 592 | function (cotire_get_source_files_compile_definitions _config _language _definitionsVar) 593 | set (_configDefinitions "") 594 | foreach (_sourceFile ${ARGN}) 595 | cotire_get_source_compile_definitions("${_config}" "${_language}" "${_sourceFile}" _sourceDefinitions) 596 | if (_sourceDefinitions) 597 | list (APPEND _configDefinitions "${_sourceFile}" ${_sourceDefinitions} "-") 598 | endif() 599 | endforeach() 600 | set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) 601 | endfunction() 602 | 603 | function (cotire_get_source_undefs _sourceFile _property _sourceUndefsVar) 604 | set (_sourceUndefs "") 605 | if (NOT CMAKE_SCRIPT_MODE_FILE) 606 | get_source_file_property(_undefs "${_sourceFile}" ${_property}) 607 | if (_undefs) 608 | list (APPEND _sourceUndefs ${_undefs}) 609 | endif() 610 | endif() 611 | cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+$" _undefs ${ARGN}) 612 | if (_undefs) 613 | list (APPEND _sourceUndefs ${_undefs}) 614 | endif() 615 | if (COTIRE_DEBUG AND _sourceUndefs) 616 | message (STATUS "Source ${_sourceFile} ${_property} undefs ${_sourceUndefs}") 617 | endif() 618 | set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) 619 | endfunction() 620 | 621 | function (cotire_get_source_files_undefs _property _sourceUndefsVar) 622 | set (_sourceUndefs "") 623 | foreach (_sourceFile ${ARGN}) 624 | cotire_get_source_undefs("${_sourceFile}" ${_property} _undefs) 625 | if (_undefs) 626 | list (APPEND _sourceUndefs "${_sourceFile}" ${_undefs} "-") 627 | endif() 628 | endforeach() 629 | set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) 630 | endfunction() 631 | 632 | macro (cotire_set_cmd_to_prologue _cmdVar) 633 | set (${_cmdVar} "${CMAKE_COMMAND}") 634 | list (APPEND ${_cmdVar} "-DCOTIRE_BUILD_TYPE:STRING=$") 635 | if (COTIRE_VERBOSE) 636 | list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=ON") 637 | elseif("${CMAKE_GENERATOR}" MATCHES "Makefiles") 638 | list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=$(VERBOSE)") 639 | endif() 640 | endmacro() 641 | 642 | function (cotire_init_compile_cmd _cmdVar _language _compilerExe _compilerArg1) 643 | if (NOT _compilerExe) 644 | set (_compilerExe "${CMAKE_${_language}_COMPILER") 645 | endif() 646 | if (NOT _compilerArg1) 647 | set (_compilerArg1 ${CMAKE_${_language}_COMPILER_ARG1}) 648 | endif() 649 | string (STRIP "${_compilerArg1}" _compilerArg1) 650 | set (${_cmdVar} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE) 651 | endfunction() 652 | 653 | macro (cotire_add_definitions_to_cmd _cmdVar) 654 | foreach (_definition ${ARGN}) 655 | if (MSVC) 656 | list (APPEND ${_cmdVar} "/D${_definition}") 657 | else() 658 | list (APPEND ${_cmdVar} "-D${_definition}") 659 | endif() 660 | endforeach() 661 | endmacro() 662 | 663 | macro (cotire_add_includes_to_cmd _cmdVar) 664 | foreach (_include ${ARGN}) 665 | if (MSVC) 666 | file (TO_NATIVE_PATH "${_include}" _include) 667 | list (APPEND ${_cmdVar} "/I${_include}") 668 | else() 669 | list (APPEND ${_cmdVar} "-I${_include}") 670 | endif() 671 | endforeach() 672 | endmacro() 673 | 674 | macro (cotire_add_compile_flags_to_cmd _cmdVar) 675 | foreach (_flag ${ARGN}) 676 | list (APPEND ${_cmdVar} "${_flag}") 677 | endforeach() 678 | endmacro() 679 | 680 | function (cotire_check_file_up_to_date _fileIsUpToDateVar _file) 681 | set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE) 682 | set (_triggerFile "") 683 | foreach (_dependencyFile ${ARGN}) 684 | if (EXISTS "${_dependencyFile}" AND "${_dependencyFile}" IS_NEWER_THAN "${_file}") 685 | set (_triggerFile "${_dependencyFile}") 686 | break() 687 | endif() 688 | endforeach() 689 | get_filename_component(_fileName "${_file}" NAME) 690 | if (EXISTS "${_file}") 691 | if (_triggerFile) 692 | if (COTIRE_VERBOSE) 693 | message (STATUS "${_fileName} update triggered by ${_triggerFile} change.") 694 | endif() 695 | else() 696 | if (COTIRE_VERBOSE) 697 | message (STATUS "${_fileName} is up-to-date.") 698 | endif() 699 | set (${_fileIsUpToDateVar} TRUE PARENT_SCOPE) 700 | endif() 701 | else() 702 | if (COTIRE_VERBOSE) 703 | message (STATUS "${_fileName} does not exist yet.") 704 | endif() 705 | endif() 706 | endfunction() 707 | 708 | macro (cotire_find_closest_relative_path _headerFile _includeDirs _relPathVar) 709 | set (${_relPathVar} "") 710 | foreach (_includeDir ${_includeDirs}) 711 | if (IS_DIRECTORY "${_includeDir}") 712 | file (RELATIVE_PATH _relPath "${_includeDir}" "${_headerFile}") 713 | if (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.") 714 | string (LENGTH "${${_relPathVar}}" _closestLen) 715 | string (LENGTH "${_relPath}" _relLen) 716 | if (_closestLen EQUAL 0 OR _relLen LESS _closestLen) 717 | set (${_relPathVar} "${_relPath}") 718 | endif() 719 | endif() 720 | elseif ("${_includeDir}" STREQUAL "${_headerFile}") 721 | # if path matches exactly, return short non-empty string 722 | set (${_relPathVar} "1") 723 | break() 724 | endif() 725 | endforeach() 726 | endmacro() 727 | 728 | macro (cotire_check_header_file_location _headerFile _insideIncudeDirs _outsideIncudeDirs _headerIsInside) 729 | # check header path against ignored and honored include directories 730 | cotire_find_closest_relative_path("${_headerFile}" "${_insideIncudeDirs}" _insideRelPath) 731 | if (_insideRelPath) 732 | # header is inside, but could be become outside if there is a shorter outside match 733 | cotire_find_closest_relative_path("${_headerFile}" "${_outsideIncudeDirs}" _outsideRelPath) 734 | if (_outsideRelPath) 735 | string (LENGTH "${_insideRelPath}" _insideRelPathLen) 736 | string (LENGTH "${_outsideRelPath}" _outsideRelPathLen) 737 | if (_outsideRelPathLen LESS _insideRelPathLen) 738 | set (${_headerIsInside} FALSE) 739 | else() 740 | set (${_headerIsInside} TRUE) 741 | endif() 742 | else() 743 | set (${_headerIsInside} TRUE) 744 | endif() 745 | else() 746 | # header is outside 747 | set (${_headerIsInside} FALSE) 748 | endif() 749 | endmacro() 750 | 751 | macro (cotire_check_ignore_header_file_path _headerFile _headerIsIgnoredVar) 752 | if (NOT EXISTS "${_headerFile}") 753 | set (${_headerIsIgnoredVar} TRUE) 754 | elseif (IS_DIRECTORY "${_headerFile}") 755 | set (${_headerIsIgnoredVar} TRUE) 756 | elseif ("${_headerFile}" MATCHES "\\.\\.|[_-]fixed" AND "${_headerFile}" MATCHES "\\.h$") 757 | # heuristic: ignore C headers with embedded parent directory references or "-fixed" or "_fixed" in path 758 | # these often stem from using GCC #include_next tricks, which may break the precompiled header compilation 759 | # with the error message "error: no include path in which to search for header.h" 760 | set (${_headerIsIgnoredVar} TRUE) 761 | else() 762 | set (${_headerIsIgnoredVar} FALSE) 763 | endif() 764 | endmacro() 765 | 766 | macro (cotire_check_ignore_header_file_ext _headerFile _ignoreExtensionsVar _headerIsIgnoredVar) 767 | # check header file extension 768 | cotire_get_source_file_extension("${_headerFile}" _headerFileExt) 769 | set (${_headerIsIgnoredVar} FALSE) 770 | if (_headerFileExt) 771 | list (FIND ${_ignoreExtensionsVar} "${_headerFileExt}" _index) 772 | if (_index GREATER -1) 773 | set (${_headerIsIgnoredVar} TRUE) 774 | endif() 775 | endif() 776 | endmacro() 777 | 778 | macro (cotire_parse_line _line _headerFileVar _headerDepthVar) 779 | if (MSVC) 780 | # cl.exe /showIncludes output looks different depending on the language pack used, e.g.: 781 | # English: "Note: including file: C:\directory\file" 782 | # German: "Hinweis: Einlesen der Datei: C:\directory\file" 783 | # We use a very general regular expression, relying on the presence of the : characters 784 | if ("${_line}" MATCHES ":( +)([^:]+:[^:]+)$") 785 | # Visual Studio compiler output 786 | string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) 787 | get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" ABSOLUTE) 788 | else() 789 | set (${_headerFileVar} "") 790 | set (${_headerDepthVar} 0) 791 | endif() 792 | else() 793 | if ("${_line}" MATCHES "^(\\.+) (.*)$") 794 | # GCC like output 795 | string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) 796 | if (IS_ABSOLUTE "${CMAKE_MATCH_2}") 797 | set (${_headerFileVar} "${CMAKE_MATCH_2}") 798 | else() 799 | get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" REALPATH) 800 | endif() 801 | else() 802 | set (${_headerFileVar} "") 803 | set (${_headerDepthVar} 0) 804 | endif() 805 | endif() 806 | endmacro() 807 | 808 | function (cotire_parse_includes _language _scanOutput _ignoredIncudeDirs _honoredIncudeDirs _ignoredExtensions _selectedIncludesVar _unparsedLinesVar) 809 | if (WIN32) 810 | # prevent CMake macro invocation errors due to backslash characters in Windows paths 811 | string (REPLACE "\\" "/" _scanOutput "${_scanOutput}") 812 | endif() 813 | # canonize slashes 814 | string (REPLACE "//" "/" _scanOutput "${_scanOutput}") 815 | # prevent semicolon from being interpreted as a line separator 816 | string (REPLACE ";" "\\;" _scanOutput "${_scanOutput}") 817 | # then separate lines 818 | string (REGEX REPLACE "\n" ";" _scanOutput "${_scanOutput}") 819 | list (LENGTH _scanOutput _len) 820 | # remove duplicate lines to speed up parsing 821 | list (REMOVE_DUPLICATES _scanOutput) 822 | list (LENGTH _scanOutput _uniqueLen) 823 | if (COTIRE_VERBOSE) 824 | message (STATUS "Scanning ${_uniqueLen} unique lines of ${_len} for includes") 825 | if (_ignoredExtensions) 826 | message (STATUS "Ignored extensions: ${_ignoredExtensions}") 827 | endif() 828 | if (_ignoredIncudeDirs) 829 | message (STATUS "Ignored paths: ${_ignoredIncudeDirs}") 830 | endif() 831 | if (_honoredIncudeDirs) 832 | message (STATUS "Included paths: ${_honoredIncudeDirs}") 833 | endif() 834 | endif() 835 | set (_sourceFiles ${ARGN}) 836 | set (_selectedIncludes "") 837 | set (_unparsedLines "") 838 | # stack keeps track of inside/outside project status of processed header files 839 | set (_headerIsInsideStack "") 840 | foreach (_line IN LISTS _scanOutput) 841 | if (_line) 842 | cotire_parse_line("${_line}" _headerFile _headerDepth) 843 | if (_headerFile) 844 | cotire_check_header_file_location("${_headerFile}" "${_ignoredIncudeDirs}" "${_honoredIncudeDirs}" _headerIsInside) 845 | if (COTIRE_DEBUG) 846 | message (STATUS "${_headerDepth}: ${_headerFile} ${_headerIsInside}") 847 | endif() 848 | # update stack 849 | list (LENGTH _headerIsInsideStack _stackLen) 850 | if (_headerDepth GREATER _stackLen) 851 | math (EXPR _stackLen "${_stackLen} + 1") 852 | foreach (_index RANGE ${_stackLen} ${_headerDepth}) 853 | list (APPEND _headerIsInsideStack ${_headerIsInside}) 854 | endforeach() 855 | else() 856 | foreach (_index RANGE ${_headerDepth} ${_stackLen}) 857 | list (REMOVE_AT _headerIsInsideStack -1) 858 | endforeach() 859 | list (APPEND _headerIsInsideStack ${_headerIsInside}) 860 | endif() 861 | if (COTIRE_DEBUG) 862 | message (STATUS "${_headerIsInsideStack}") 863 | endif() 864 | # header is a candidate if it is outside project 865 | if (NOT _headerIsInside) 866 | # get parent header file's inside/outside status 867 | if (_headerDepth GREATER 1) 868 | math (EXPR _index "${_headerDepth} - 2") 869 | list (GET _headerIsInsideStack ${_index} _parentHeaderIsInside) 870 | else() 871 | set (_parentHeaderIsInside TRUE) 872 | endif() 873 | # select header file if parent header file is inside project 874 | # (e.g., a project header file that includes a standard header file) 875 | if (_parentHeaderIsInside) 876 | cotire_check_ignore_header_file_path("${_headerFile}" _headerIsIgnored) 877 | if (NOT _headerIsIgnored) 878 | cotire_check_ignore_header_file_ext("${_headerFile}" _ignoredExtensions _headerIsIgnored) 879 | if (NOT _headerIsIgnored) 880 | list (APPEND _selectedIncludes "${_headerFile}") 881 | else() 882 | # fix header's inside status on stack, it is ignored by extension now 883 | list (REMOVE_AT _headerIsInsideStack -1) 884 | list (APPEND _headerIsInsideStack TRUE) 885 | endif() 886 | endif() 887 | if (COTIRE_DEBUG) 888 | message (STATUS "${_headerFile} ${_ignoredExtensions} ${_headerIsIgnored}") 889 | endif() 890 | endif() 891 | endif() 892 | else() 893 | if (MSVC) 894 | # for cl.exe do not keep unparsed lines which solely consist of a source file name 895 | string (FIND "${_sourceFiles}" "${_line}" _index) 896 | if (_index LESS 0) 897 | list (APPEND _unparsedLines "${_line}") 898 | endif() 899 | else() 900 | list (APPEND _unparsedLines "${_line}") 901 | endif() 902 | endif() 903 | endif() 904 | endforeach() 905 | list (REMOVE_DUPLICATES _selectedIncludes) 906 | set (${_selectedIncludesVar} ${_selectedIncludes} PARENT_SCOPE) 907 | set (${_unparsedLinesVar} ${_unparsedLines} PARENT_SCOPE) 908 | endfunction() 909 | 910 | function (cotire_scan_includes _includesVar) 911 | set(_options "") 912 | set(_oneValueArgs COMPILER_ID COMPILER_EXECUTABLE COMPILER_VERSION LANGUAGE UNPARSED_LINES) 913 | set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS) 914 | cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) 915 | set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) 916 | if (NOT _option_LANGUAGE) 917 | set (_option_LANGUAGE "CXX") 918 | endif() 919 | if (NOT _option_COMPILER_ID) 920 | set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") 921 | endif() 922 | set (_cmd "${_option_COMPILER_EXECUTABLE}" ${_option_COMPILER_ARG1}) 923 | cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") 924 | cotire_add_definitions_to_cmd(_cmd ${_option_COMPILE_DEFINITIONS}) 925 | cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) 926 | cotire_add_includes_to_cmd(_cmd ${_option_INCLUDE_DIRECTORIES}) 927 | cotire_add_makedep_flags("${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" _cmd) 928 | # only consider existing source files for scanning 929 | set (_existingSourceFiles "") 930 | foreach (_sourceFile ${_sourceFiles}) 931 | if (EXISTS "${_sourceFile}") 932 | list (APPEND _existingSourceFiles "${_sourceFile}") 933 | endif() 934 | endforeach() 935 | if (NOT _existingSourceFiles) 936 | set (${_includesVar} "" PARENT_SCOPE) 937 | return() 938 | endif() 939 | list (APPEND _cmd ${_existingSourceFiles}) 940 | if (COTIRE_VERBOSE) 941 | message (STATUS "execute_process: ${_cmd}") 942 | endif() 943 | if ("${_option_COMPILER_ID}" STREQUAL "MSVC") 944 | if (COTIRE_DEBUG) 945 | message (STATUS "clearing VS_UNICODE_OUTPUT") 946 | endif() 947 | # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared 948 | unset (ENV{VS_UNICODE_OUTPUT}) 949 | endif() 950 | execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 951 | RESULT_VARIABLE _result OUTPUT_QUIET ERROR_VARIABLE _output) 952 | if (_result) 953 | message (STATUS "Result ${_result} scanning includes of ${_existingSourceFiles}.") 954 | endif() 955 | cotire_parse_includes( 956 | "${_option_LANGUAGE}" "${_output}" 957 | "${_option_IGNORE_PATH}" "${_option_INCLUDE_PATH}" 958 | "${_option_IGNORE_EXTENSIONS}" 959 | _includes _unparsedLines 960 | ${_sourceFiles}) 961 | set (${_includesVar} ${_includes} PARENT_SCOPE) 962 | if (_option_UNPARSED_LINES) 963 | set (${_option_UNPARSED_LINES} ${_unparsedLines} PARENT_SCOPE) 964 | endif() 965 | endfunction() 966 | 967 | macro (cotire_append_undefs _contentsVar) 968 | set (_undefs ${ARGN}) 969 | if (_undefs) 970 | list (REMOVE_DUPLICATES _undefs) 971 | foreach (_definition ${_undefs}) 972 | list (APPEND ${_contentsVar} "#undef ${_definition}") 973 | endforeach() 974 | endif() 975 | endmacro() 976 | 977 | macro (cotire_comment_str _language _commentText _commentVar) 978 | if ("${_language}" STREQUAL "CMAKE") 979 | set (${_commentVar} "# ${_commentText}") 980 | else() 981 | set (${_commentVar} "/* ${_commentText} */") 982 | endif() 983 | endmacro() 984 | 985 | function (cotire_write_file _language _file _contents _force) 986 | get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) 987 | cotire_comment_str("${_language}" "${_moduleName} ${COTIRE_CMAKE_MODULE_VERSION} generated file" _header1) 988 | cotire_comment_str("${_language}" "${_file}" _header2) 989 | set (_contents "${_header1}\n${_header2}\n${_contents}") 990 | if (COTIRE_DEBUG) 991 | message (STATUS "${_contents}") 992 | endif() 993 | if (_force OR NOT EXISTS "${_file}") 994 | file (WRITE "${_file}" "${_contents}") 995 | else() 996 | file (READ "${_file}" _oldContents) 997 | if (NOT "${_oldContents}" STREQUAL "${_contents}") 998 | file (WRITE "${_file}" "${_contents}") 999 | else() 1000 | if (COTIRE_DEBUG) 1001 | message (STATUS "${_file} unchanged") 1002 | endif() 1003 | endif() 1004 | endif() 1005 | endfunction() 1006 | 1007 | function (cotire_generate_unity_source _unityFile) 1008 | set(_options "") 1009 | set(_oneValueArgs LANGUAGE) 1010 | set(_multiValueArgs 1011 | DEPENDS SOURCES_COMPILE_DEFINITIONS 1012 | PRE_UNDEFS SOURCES_PRE_UNDEFS POST_UNDEFS SOURCES_POST_UNDEFS) 1013 | cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) 1014 | if (_option_DEPENDS) 1015 | cotire_check_file_up_to_date(_unityFileIsUpToDate "${_unityFile}" ${_option_DEPENDS}) 1016 | if (_unityFileIsUpToDate) 1017 | return() 1018 | endif() 1019 | endif() 1020 | set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) 1021 | if (NOT _option_PRE_UNDEFS) 1022 | set (_option_PRE_UNDEFS "") 1023 | endif() 1024 | if (NOT _option_SOURCES_PRE_UNDEFS) 1025 | set (_option_SOURCES_PRE_UNDEFS "") 1026 | endif() 1027 | if (NOT _option_POST_UNDEFS) 1028 | set (_option_POST_UNDEFS "") 1029 | endif() 1030 | if (NOT _option_SOURCES_POST_UNDEFS) 1031 | set (_option_SOURCES_POST_UNDEFS "") 1032 | endif() 1033 | set (_contents "") 1034 | if (_option_LANGUAGE AND _sourceFiles) 1035 | if ("${_option_LANGUAGE}" STREQUAL "CXX") 1036 | list (APPEND _contents "#ifdef __cplusplus") 1037 | elseif ("${_option_LANGUAGE}" STREQUAL "C") 1038 | list (APPEND _contents "#ifndef __cplusplus") 1039 | endif() 1040 | endif() 1041 | set (_compileUndefinitions "") 1042 | foreach (_sourceFile ${_sourceFiles}) 1043 | cotire_get_source_compile_definitions( 1044 | "${_option_CONFIGURATION}" "${_option_LANGUAGE}" "${_sourceFile}" _compileDefinitions 1045 | ${_option_SOURCES_COMPILE_DEFINITIONS}) 1046 | cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_PRE_UNDEFS _sourcePreUndefs ${_option_SOURCES_PRE_UNDEFS}) 1047 | cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_POST_UNDEFS _sourcePostUndefs ${_option_SOURCES_POST_UNDEFS}) 1048 | if (_option_PRE_UNDEFS) 1049 | list (APPEND _compileUndefinitions ${_option_PRE_UNDEFS}) 1050 | endif() 1051 | if (_sourcePreUndefs) 1052 | list (APPEND _compileUndefinitions ${_sourcePreUndefs}) 1053 | endif() 1054 | if (_compileUndefinitions) 1055 | cotire_append_undefs(_contents ${_compileUndefinitions}) 1056 | set (_compileUndefinitions "") 1057 | endif() 1058 | if (_sourcePostUndefs) 1059 | list (APPEND _compileUndefinitions ${_sourcePostUndefs}) 1060 | endif() 1061 | if (_option_POST_UNDEFS) 1062 | list (APPEND _compileUndefinitions ${_option_POST_UNDEFS}) 1063 | endif() 1064 | foreach (_definition ${_compileDefinitions}) 1065 | if ("${_definition}" MATCHES "^([a-zA-Z0-9_]+)=(.+)$") 1066 | list (APPEND _contents "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}") 1067 | list (INSERT _compileUndefinitions 0 "${CMAKE_MATCH_1}") 1068 | else() 1069 | list (APPEND _contents "#define ${_definition}") 1070 | list (INSERT _compileUndefinitions 0 "${_definition}") 1071 | endif() 1072 | endforeach() 1073 | get_filename_component(_sourceFile "${_sourceFile}" ABSOLUTE) 1074 | if (WIN32) 1075 | file (TO_NATIVE_PATH "${_sourceFile}" _sourceFile) 1076 | endif() 1077 | list (APPEND _contents "#include \"${_sourceFile}\"") 1078 | endforeach() 1079 | if (_compileUndefinitions) 1080 | cotire_append_undefs(_contents ${_compileUndefinitions}) 1081 | set (_compileUndefinitions "") 1082 | endif() 1083 | if (_option_LANGUAGE AND _sourceFiles) 1084 | list (APPEND _contents "#endif") 1085 | endif() 1086 | list (APPEND _contents "") 1087 | string (REPLACE ";" "\n" _contents "${_contents}") 1088 | if (COTIRE_VERBOSE) 1089 | message ("${_contents}") 1090 | endif() 1091 | cotire_write_file("${_option_LANGUAGE}" "${_unityFile}" "${_contents}" TRUE) 1092 | endfunction() 1093 | 1094 | function (cotire_generate_prefix_header _prefixFile) 1095 | set(_options "") 1096 | set(_oneValueArgs LANGUAGE COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION) 1097 | set(_multiValueArgs DEPENDS COMPILE_DEFINITIONS COMPILE_FLAGS 1098 | INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS) 1099 | cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) 1100 | if (_option_DEPENDS) 1101 | cotire_check_file_up_to_date(_prefixFileIsUpToDate "${_prefixFile}" ${_option_DEPENDS}) 1102 | if (_prefixFileIsUpToDate) 1103 | return() 1104 | endif() 1105 | endif() 1106 | set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) 1107 | cotire_scan_includes(_selectedHeaders ${_sourceFiles} 1108 | LANGUAGE "${_option_LANGUAGE}" 1109 | COMPILER_EXECUTABLE "${_option_COMPILER_EXECUTABLE}" 1110 | COMPILER_ID "${_option_COMPILER_ID}" 1111 | COMPILER_VERSION "${_option_COMPILER_VERSION}" 1112 | COMPILE_DEFINITIONS ${_option_COMPILE_DEFINITIONS} 1113 | COMPILE_FLAGS ${_option_COMPILE_FLAGS} 1114 | INCLUDE_DIRECTORIES ${_option_INCLUDE_DIRECTORIES} 1115 | IGNORE_PATH ${_option_IGNORE_PATH} 1116 | INCLUDE_PATH ${_option_INCLUDE_PATH} 1117 | IGNORE_EXTENSIONS ${_option_IGNORE_EXTENSIONS} 1118 | UNPARSED_LINES _unparsedLines) 1119 | cotire_generate_unity_source("${_prefixFile}" LANGUAGE "${_option_LANGUAGE}" ${_selectedHeaders}) 1120 | set (_unparsedLinesFile "${_prefixFile}.log") 1121 | if (_unparsedLines) 1122 | if (COTIRE_VERBOSE OR NOT _selectedHeaders) 1123 | list (LENGTH _unparsedLines _skippedLineCount) 1124 | file (RELATIVE_PATH _unparsedLinesFileRelPath "${CMAKE_BINARY_DIR}" "${_unparsedLinesFile}") 1125 | message (STATUS "${_skippedLineCount} line(s) skipped, see ${_unparsedLinesFileRelPath}") 1126 | endif() 1127 | string (REPLACE ";" "\n" _unparsedLines "${_unparsedLines}") 1128 | file (WRITE "${_unparsedLinesFile}" "${_unparsedLines}\n") 1129 | else() 1130 | file (REMOVE "${_unparsedLinesFile}") 1131 | endif() 1132 | endfunction() 1133 | 1134 | function (cotire_add_makedep_flags _language _compilerID _compilerVersion _flagsVar) 1135 | set (_flags ${${_flagsVar}}) 1136 | if ("${_compilerID}" STREQUAL "MSVC") 1137 | # cl.exe options used 1138 | # /nologo suppresses display of sign-on banner 1139 | # /TC treat all files named on the command line as C source files 1140 | # /TP treat all files named on the command line as C++ source files 1141 | # /EP preprocess to stdout without #line directives 1142 | # /showIncludes list include files 1143 | set (_sourceFileTypeC "/TC") 1144 | set (_sourceFileTypeCXX "/TP") 1145 | if (_flags) 1146 | # append to list 1147 | list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /showIncludes) 1148 | else() 1149 | # return as a flag string 1150 | set (_flags "${_sourceFileType${_language}} /EP /showIncludes") 1151 | endif() 1152 | elseif ("${_compilerID}" STREQUAL "GNU") 1153 | # GCC options used 1154 | # -H print the name of each header file used 1155 | # -E invoke preprocessor 1156 | # -fdirectives-only do not expand macros, requires GCC >= 4.3 1157 | if (_flags) 1158 | # append to list 1159 | list (APPEND _flags -H -E) 1160 | if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") 1161 | list (APPEND _flags "-fdirectives-only") 1162 | endif() 1163 | else() 1164 | # return as a flag string 1165 | set (_flags "-H -E") 1166 | if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") 1167 | set (_flags "${_flags} -fdirectives-only") 1168 | endif() 1169 | endif() 1170 | elseif ("${_compilerID}" STREQUAL "Clang") 1171 | # Clang options used 1172 | # -H print the name of each header file used 1173 | # -E invoke preprocessor 1174 | if (_flags) 1175 | # append to list 1176 | list (APPEND _flags -H -E) 1177 | else() 1178 | # return as a flag string 1179 | set (_flags "-H -E") 1180 | endif() 1181 | else() 1182 | message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") 1183 | endif() 1184 | set (${_flagsVar} ${_flags} PARENT_SCOPE) 1185 | endfunction() 1186 | 1187 | function (cotire_add_pch_compilation_flags _language _compilerID _compilerVersion _prefixFile _pchFile _hostFile _flagsVar) 1188 | set (_flags ${${_flagsVar}}) 1189 | if ("${_compilerID}" STREQUAL "MSVC") 1190 | file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) 1191 | file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) 1192 | file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) 1193 | # cl.exe options used 1194 | # /Yc creates a precompiled header file 1195 | # /Fp specifies precompiled header binary file name 1196 | # /FI forces inclusion of file 1197 | # /TC treat all files named on the command line as C source files 1198 | # /TP treat all files named on the command line as C++ source files 1199 | # /Zs syntax check only 1200 | set (_sourceFileTypeC "/TC") 1201 | set (_sourceFileTypeCXX "/TP") 1202 | if (_flags) 1203 | # append to list 1204 | list (APPEND _flags /nologo "${_sourceFileType${_language}}" 1205 | "/Yc${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") 1206 | else() 1207 | # return as a flag string 1208 | set (_flags "/Yc\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") 1209 | endif() 1210 | elseif ("${_compilerID}" MATCHES "GNU|Clang") 1211 | # GCC / Clang options used 1212 | # -x specify the source language 1213 | # -c compile but do not link 1214 | # -o place output in file 1215 | set (_xLanguage_C "c-header") 1216 | set (_xLanguage_CXX "c++-header") 1217 | if (_flags) 1218 | # append to list 1219 | list (APPEND _flags "-x" "${_xLanguage_${_language}}" "-c" "${_prefixFile}" -o "${_pchFile}") 1220 | else() 1221 | # return as a flag string 1222 | set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"") 1223 | endif() 1224 | else() 1225 | message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") 1226 | endif() 1227 | set (${_flagsVar} ${_flags} PARENT_SCOPE) 1228 | endfunction() 1229 | 1230 | function (cotire_add_pch_inclusion_flags _language _compilerID _compilerVersion _prefixFile _pchFile _flagsVar) 1231 | set (_flags ${${_flagsVar}}) 1232 | if ("${_compilerID}" STREQUAL "MSVC") 1233 | file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) 1234 | file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) 1235 | # cl.exe options used 1236 | # /Yu uses a precompiled header file during build 1237 | # /Fp specifies precompiled header binary file name 1238 | # /FI forces inclusion of file 1239 | if (_flags) 1240 | # append to list 1241 | list (APPEND _flags "/Yu${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") 1242 | else() 1243 | # return as a flag string 1244 | set (_flags "/Yu\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") 1245 | endif() 1246 | elseif ("${_compilerID}" STREQUAL "GNU") 1247 | # GCC options used 1248 | # -include process include file as the first line of the primary source file 1249 | # -Winvalid-pch warns if precompiled header is found but cannot be used 1250 | if (_flags) 1251 | # append to list 1252 | list (APPEND _flags "-include" "${_prefixFile}" "-Winvalid-pch") 1253 | else() 1254 | # return as a flag string 1255 | set (_flags "-include \"${_prefixFile}\" -Winvalid-pch") 1256 | endif() 1257 | elseif ("${_compilerID}" STREQUAL "Clang") 1258 | # Clang options used 1259 | # -include process include file as the first line of the primary source file 1260 | # -Qunused-arguments don't emit warning for unused driver arguments 1261 | if (_flags) 1262 | # append to list 1263 | list (APPEND _flags "-include" "${_prefixFile}" "-Qunused-arguments") 1264 | else() 1265 | # return as a flag string 1266 | set (_flags "-include \"${_prefixFile}\" -Qunused-arguments") 1267 | endif() 1268 | else() 1269 | message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") 1270 | endif() 1271 | set (${_flagsVar} ${_flags} PARENT_SCOPE) 1272 | endfunction() 1273 | 1274 | function (cotire_precompile_prefix_header _prefixFile _pchFile _hostFile) 1275 | set(_options "") 1276 | set(_oneValueArgs COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION LANGUAGE) 1277 | set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES) 1278 | cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) 1279 | if (NOT _option_LANGUAGE) 1280 | set (_option_LANGUAGE "CXX") 1281 | endif() 1282 | if (NOT _option_COMPILER_ID) 1283 | set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") 1284 | endif() 1285 | cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") 1286 | cotire_add_definitions_to_cmd(_cmd ${_option_COMPILE_DEFINITIONS}) 1287 | cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) 1288 | cotire_add_includes_to_cmd(_cmd ${_option_INCLUDE_DIRECTORIES}) 1289 | cotire_add_pch_compilation_flags( 1290 | "${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" 1291 | "${_prefixFile}" "${_pchFile}" "${_hostFile}" _cmd) 1292 | if (COTIRE_VERBOSE) 1293 | message (STATUS "execute_process: ${_cmd}") 1294 | endif() 1295 | if ("${_option_COMPILER_ID}" STREQUAL "MSVC") 1296 | if (COTIRE_DEBUG) 1297 | message (STATUS "clearing VS_UNICODE_OUTPUT") 1298 | endif() 1299 | # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared 1300 | unset (ENV{VS_UNICODE_OUTPUT}) 1301 | endif() 1302 | execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE _result) 1303 | if (_result) 1304 | message (FATAL_ERROR "Error ${_result} precompiling ${_prefixFile}.") 1305 | endif() 1306 | endfunction() 1307 | 1308 | function (cotire_check_precompiled_header_support _language _targetSourceDir _target _msgVar) 1309 | if (MSVC) 1310 | # supported since Visual Studio C++ 6.0 1311 | # and CMake does not support an earlier version 1312 | set (${_msgVar} "" PARENT_SCOPE) 1313 | elseif ("${CMAKE_${_language}_COMPILER_ID}" STREQUAL "GNU") 1314 | # GCC PCH support requires GCC >= 3.4 1315 | cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) 1316 | if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND 1317 | "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "3.4.0") 1318 | set (${_msgVar} 1319 | "Precompiled headers not supported for ${_language} compiler ${CMAKE_${_language}_COMPILER_ID} version ${COTIRE_${_language}_COMPILER_VERSION}." 1320 | PARENT_SCOPE) 1321 | else() 1322 | set (${_msgVar} "" PARENT_SCOPE) 1323 | endif() 1324 | elseif ("${CMAKE_${_language}_COMPILER_ID}" STREQUAL "Clang") 1325 | # Clang has PCH support 1326 | set (${_msgVar} "" PARENT_SCOPE) 1327 | else() 1328 | set (${_msgVar} "Unsupported ${_language} compiler ${CMAKE_${_language}_COMPILER_ID}." PARENT_SCOPE) 1329 | endif() 1330 | if (APPLE) 1331 | # PCH compilation not supported by GCC / Clang for multi-architecture builds (e.g., i386, x86_64) 1332 | if (CMAKE_CONFIGURATION_TYPES) 1333 | set (_configs ${CMAKE_CONFIGURATION_TYPES}) 1334 | elseif (CMAKE_BUILD_TYPE) 1335 | set (_configs ${CMAKE_BUILD_TYPE}) 1336 | else() 1337 | set (_configs "None") 1338 | endif() 1339 | foreach (_config ${_configs}) 1340 | cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags) 1341 | cotire_filter_compile_flags("arch" _architectures _ignore ${_targetFlags}) 1342 | list (LENGTH _architectures _numberOfArchitectures) 1343 | if (_numberOfArchitectures GREATER 1) 1344 | string (REPLACE ";" ", " _architectureStr "${_architectures}") 1345 | set (${_msgVar} 1346 | "Precompiled headers not supported on Darwin for multi-architecture builds (${_architectureStr})." 1347 | PARENT_SCOPE) 1348 | break() 1349 | endif() 1350 | endforeach() 1351 | endif() 1352 | endfunction() 1353 | 1354 | macro (cotire_get_intermediate_dir _cotireDir) 1355 | get_filename_component(${_cotireDir} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${COTIRE_INTDIR}" ABSOLUTE) 1356 | endmacro() 1357 | 1358 | function (cotire_make_untiy_source_file_paths _language _target _maxIncludes _unityFilesVar) 1359 | set (_sourceFiles ${ARGN}) 1360 | list (LENGTH _sourceFiles _numberOfSources) 1361 | set (_unityFileExt_C ".c") 1362 | set (_unityFileExt_CXX ".cxx") 1363 | if (NOT DEFINED _unityFileExt_${_language}) 1364 | set (${_unityFileVar} "" PARENT_SCOPE) 1365 | return() 1366 | endif() 1367 | set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") 1368 | cotire_get_intermediate_dir(_baseDir) 1369 | set (_startIndex 0) 1370 | set (_index 0) 1371 | set (_unityFiles "") 1372 | foreach (_sourceFile ${_sourceFiles}) 1373 | get_source_file_property(_startNew "${_sourceFile}" COTIRE_START_NEW_UNITY_SOURCE) 1374 | math (EXPR _unityFileCount "${_index} - ${_startIndex}") 1375 | if (_startNew OR (_maxIncludes GREATER 0 AND NOT _unityFileCount LESS _maxIncludes)) 1376 | if (_index GREATER 0) 1377 | math (EXPR _endIndex "${_index} - 1") 1378 | set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") 1379 | list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") 1380 | endif() 1381 | set (_startIndex ${_index}) 1382 | endif() 1383 | math (EXPR _index "${_index} + 1") 1384 | endforeach() 1385 | if (_startIndex EQUAL 0) 1386 | set (_unityFileName "${_unityFileBaseName}${_unityFileExt_${_language}}") 1387 | list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") 1388 | elseif (_startIndex LESS _numberOfSources) 1389 | math (EXPR _endIndex "${_index} - 1") 1390 | set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") 1391 | list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") 1392 | endif() 1393 | set (${_unityFilesVar} ${_unityFiles} PARENT_SCOPE) 1394 | if (COTIRE_DEBUG) 1395 | message(STATUS "${_unityFiles}") 1396 | endif() 1397 | endfunction() 1398 | 1399 | function (cotire_make_prefix_file_name _language _target _prefixFileBaseNameVar _prefixFileNameVar) 1400 | set (_prefixFileExt_C ".h") 1401 | set (_prefixFileExt_CXX ".hxx") 1402 | if (NOT _language) 1403 | set (_prefixFileBaseName "${_target}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") 1404 | set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_C}") 1405 | elseif (DEFINED _prefixFileExt_${_language}) 1406 | set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") 1407 | set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_${_language}}") 1408 | else() 1409 | set (_prefixFileBaseName "") 1410 | set (_prefixFileName "") 1411 | endif() 1412 | set (${_prefixFileBaseNameVar} "${_prefixFileBaseName}" PARENT_SCOPE) 1413 | set (${_prefixFileNameVar} "${_prefixFileName}" PARENT_SCOPE) 1414 | endfunction() 1415 | 1416 | function (cotire_make_prefix_file_path _language _target _prefixFileVar) 1417 | cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) 1418 | set (${_prefixFileVar} "" PARENT_SCOPE) 1419 | if (_prefixFileName) 1420 | if (NOT _language) 1421 | set (_language "C") 1422 | endif() 1423 | if (MSVC OR "${CMAKE_${_language}_COMPILER_ID}" MATCHES "GNU|Clang") 1424 | cotire_get_intermediate_dir(_baseDir) 1425 | set (${_prefixFileVar} "${_baseDir}/${_prefixFileName}" PARENT_SCOPE) 1426 | endif() 1427 | endif() 1428 | endfunction() 1429 | 1430 | function (cotire_make_pch_file_path _language _targetSourceDir _target _pchFileVar) 1431 | cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) 1432 | set (${_pchFileVar} "" PARENT_SCOPE) 1433 | if (_prefixFileBaseName AND _prefixFileName) 1434 | cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _msg) 1435 | if (NOT _msg) 1436 | if (XCODE) 1437 | # For Xcode, we completely hand off the compilation of the prefix header to the IDE 1438 | return() 1439 | endif() 1440 | cotire_get_intermediate_dir(_baseDir) 1441 | if (MSVC) 1442 | # MSVC uses the extension .pch added to the prefix header base name 1443 | set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pch" PARENT_SCOPE) 1444 | elseif ("${CMAKE_${_language}_COMPILER_ID}" MATCHES "GNU|Clang") 1445 | # GCC / Clang look for a precompiled header corresponding to the prefix header with the extension .gch appended 1446 | set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.gch" PARENT_SCOPE) 1447 | endif() 1448 | endif() 1449 | endif() 1450 | endfunction() 1451 | 1452 | function (cotire_select_unity_source_files _unityFile _sourcesVar) 1453 | set (_sourceFiles ${ARGN}) 1454 | if (_sourceFiles AND "${_unityFile}" MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}_([0-9]+)_([0-9]+)") 1455 | set (_startIndex ${CMAKE_MATCH_1}) 1456 | set (_endIndex ${CMAKE_MATCH_2}) 1457 | list (LENGTH _sourceFiles _numberOfSources) 1458 | if (NOT _startIndex LESS _numberOfSources) 1459 | math (EXPR _startIndex "${_numberOfSources} - 1") 1460 | endif() 1461 | if (NOT _endIndex LESS _numberOfSources) 1462 | math (EXPR _endIndex "${_numberOfSources} - 1") 1463 | endif() 1464 | set (_files "") 1465 | foreach (_index RANGE ${_startIndex} ${_endIndex}) 1466 | list (GET _sourceFiles ${_index} _file) 1467 | list (APPEND _files "${_file}") 1468 | endforeach() 1469 | else() 1470 | set (_files ${_sourceFiles}) 1471 | endif() 1472 | set (${_sourcesVar} ${_files} PARENT_SCOPE) 1473 | endfunction() 1474 | 1475 | function (cotire_get_unity_source_dependencies _language _target _dependencySourcesVar) 1476 | set (_dependencySources "") 1477 | # depend on target's generated source files 1478 | cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${ARGN}) 1479 | if (_generatedSources) 1480 | # but omit all generated source files that have the COTIRE_EXCLUDED property set to true 1481 | cotire_get_objects_with_property_on(_excludedGeneratedSources COTIRE_EXCLUDED SOURCE ${_generatedSources}) 1482 | if (_excludedGeneratedSources) 1483 | list (REMOVE_ITEM _generatedSources ${_excludedGeneratedSources}) 1484 | endif() 1485 | # and omit all generated source files that have the COTIRE_DEPENDENCY property set to false explicitly 1486 | cotire_get_objects_with_property_off(_excludedNonDependencySources COTIRE_DEPENDENCY SOURCE ${_generatedSources}) 1487 | if (_excludedNonDependencySources) 1488 | list (REMOVE_ITEM _generatedSources ${_excludedNonDependencySources}) 1489 | endif() 1490 | if (_generatedSources) 1491 | list (APPEND _dependencySources ${_generatedSources}) 1492 | endif() 1493 | endif() 1494 | if (COTIRE_DEBUG AND _dependencySources) 1495 | message (STATUS "${_language} ${_target} unity source depends on ${_dependencySources}") 1496 | endif() 1497 | set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) 1498 | endfunction() 1499 | 1500 | function (cotire_get_prefix_header_dependencies _language _target _dependencySourcesVar) 1501 | # depend on target source files marked with custom COTIRE_DEPENDENCY property 1502 | set (_dependencySources "") 1503 | cotire_get_objects_with_property_on(_dependencySources COTIRE_DEPENDENCY SOURCE ${ARGN}) 1504 | if (COTIRE_DEBUG AND _dependencySources) 1505 | message (STATUS "${_language} ${_target} prefix header DEPENDS ${_dependencySources}") 1506 | endif() 1507 | set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) 1508 | endfunction() 1509 | 1510 | function (cotire_generate_target_script _language _configurations _targetSourceDir _targetBinaryDir _target _targetScriptVar) 1511 | set (COTIRE_TARGET_SOURCES ${ARGN}) 1512 | get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) 1513 | set (_targetCotireScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_moduleName}") 1514 | cotire_get_prefix_header_dependencies(${_language} ${_target} COTIRE_TARGET_PREFIX_DEPENDS ${COTIRE_TARGET_SOURCES}) 1515 | cotire_get_unity_source_dependencies(${_language} ${_target} COTIRE_TARGET_UNITY_DEPENDS ${COTIRE_TARGET_SOURCES}) 1516 | # set up variables to be configured 1517 | set (COTIRE_TARGET_LANGUAGE "${_language}") 1518 | cotire_determine_compiler_version("${COTIRE_TARGET_LANGUAGE}" COTIRE_${_language}_COMPILER) 1519 | get_target_property(COTIRE_TARGET_IGNORE_PATH ${_target} COTIRE_PREFIX_HEADER_IGNORE_PATH) 1520 | cotire_add_sys_root_paths(COTIRE_TARGET_IGNORE_PATH) 1521 | get_target_property(COTIRE_TARGET_INCLUDE_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PATH) 1522 | cotire_add_sys_root_paths(COTIRE_TARGET_INCLUDE_PATH) 1523 | get_target_property(COTIRE_TARGET_PRE_UNDEFS ${_target} COTIRE_UNITY_SOURCE_PRE_UNDEFS) 1524 | get_target_property(COTIRE_TARGET_POST_UNDEFS ${_target} COTIRE_UNITY_SOURCE_POST_UNDEFS) 1525 | get_target_property(COTIRE_TARGET_MAXIMUM_NUMBER_OF_INCLUDES ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) 1526 | cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_PRE_UNDEFS COTIRE_TARGET_SOURCES_PRE_UNDEFS ${COTIRE_TARGET_SOURCES}) 1527 | cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_POST_UNDEFS COTIRE_TARGET_SOURCES_POST_UNDEFS ${COTIRE_TARGET_SOURCES}) 1528 | set (COTIRE_TARGET_CONFIGURATION_TYPES "${_configurations}") 1529 | foreach (_config ${_configurations}) 1530 | string (TOUPPER "${_config}" _upperConfig) 1531 | cotire_get_target_include_directories( 1532 | "${_config}" "${_language}" "${_targetSourceDir}" "${_targetBinaryDir}" "${_target}" COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}) 1533 | cotire_get_target_compile_definitions( 1534 | "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}) 1535 | cotire_get_target_compiler_flags( 1536 | "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}) 1537 | cotire_get_source_files_compile_definitions( 1538 | "${_config}" "${_language}" COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig} ${COTIRE_TARGET_SOURCES}) 1539 | endforeach() 1540 | get_cmake_property(_vars VARIABLES) 1541 | string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+" _matchVars "${_vars}") 1542 | # remove COTIRE_VERBOSE which is passed as a CMake define on command line 1543 | list (REMOVE_ITEM _matchVars COTIRE_VERBOSE) 1544 | set (_contents "") 1545 | foreach (_var IN LISTS _matchVars ITEMS 1546 | MSVC CMAKE_GENERATOR CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES 1547 | CMAKE_${_language}_COMPILER_ID CMAKE_${_language}_COMPILER CMAKE_${_language}_COMPILER_ARG1 1548 | CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) 1549 | if (DEFINED ${_var}) 1550 | string (REPLACE "\"" "\\\"" _value "${${_var}}") 1551 | set (_contents "${_contents}set (${_var} \"${_value}\")\n") 1552 | endif() 1553 | endforeach() 1554 | cotire_write_file("CMAKE" "${_targetCotireScript}" "${_contents}" FALSE) 1555 | set (${_targetScriptVar} "${_targetCotireScript}" PARENT_SCOPE) 1556 | endfunction() 1557 | 1558 | function (cotire_setup_pch_file_compilation _language _targetSourceDir _targetScript _prefixFile _pchFile) 1559 | set (_sourceFiles ${ARGN}) 1560 | if (MSVC) 1561 | # for Visual Studio, we attach the precompiled header compilation to the first source file 1562 | # the remaining files include the precompiled header, see cotire_setup_prefix_file_inclusion 1563 | if (_sourceFiles) 1564 | file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) 1565 | file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) 1566 | list (GET _sourceFiles 0 _hostFile) 1567 | set (_flags "") 1568 | cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) 1569 | cotire_add_pch_compilation_flags( 1570 | "${_language}" "MSVC" "${COTIRE_${_language}_COMPILER_VERSION}" 1571 | "${_prefixFile}" "${_pchFile}" "${_hostFile}" _flags) 1572 | set_property (SOURCE ${_hostFile} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") 1573 | set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_OUTPUTS "${_pchFile}") 1574 | # make first source file depend on prefix header 1575 | set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}") 1576 | endif() 1577 | elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") 1578 | # for makefile based generator, we add a custom command to precompile the prefix header 1579 | if (_targetScript) 1580 | cotire_set_cmd_to_prologue(_cmds) 1581 | list (GET _sourceFiles 0 _hostFile) 1582 | list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "precompile" "${_targetScript}" "${_prefixFile}" "${_pchFile}" "${_hostFile}") 1583 | file (RELATIVE_PATH _pchFileRelPath "${CMAKE_BINARY_DIR}" "${_pchFile}") 1584 | if (COTIRE_DEBUG) 1585 | message (STATUS "add_custom_command: OUTPUT ${_pchFile} ${_cmds} DEPENDS ${_prefixFile} IMPLICIT_DEPENDS ${_language} ${_prefixFile}") 1586 | endif() 1587 | set_property (SOURCE "${_pchFile}" PROPERTY GENERATED TRUE) 1588 | add_custom_command(OUTPUT "${_pchFile}" 1589 | COMMAND ${_cmds} 1590 | DEPENDS "${_prefixFile}" 1591 | IMPLICIT_DEPENDS ${_language} "${_prefixFile}" 1592 | WORKING_DIRECTORY "${_targetSourceDir}" 1593 | COMMENT "Building ${_language} precompiled header ${_pchFileRelPath}" VERBATIM) 1594 | endif() 1595 | endif() 1596 | endfunction() 1597 | 1598 | function (cotire_setup_prefix_file_inclusion _language _target _wholeTarget _prefixFile _pchFile) 1599 | set (_sourceFiles ${ARGN}) 1600 | if (MSVC) 1601 | # for Visual Studio, we include the precompiled header in all but the first source file 1602 | # the first source file does the precompiled header compilation, see cotire_setup_pch_file_compilation 1603 | list (LENGTH _sourceFiles _numberOfSourceFiles) 1604 | if (_numberOfSourceFiles GREATER 1) 1605 | # mark sources as cotired to prevent them from being used in another cotired target 1606 | set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") 1607 | list (REMOVE_AT _sourceFiles 0) 1608 | set (_flags "") 1609 | cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) 1610 | cotire_add_pch_inclusion_flags( 1611 | "${_language}" "MSVC" "${COTIRE_${_language}_COMPILER_VERSION}" 1612 | "${_prefixFile}" "${_pchFile}" _flags) 1613 | set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") 1614 | # make source files depend on precompiled header 1615 | set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") 1616 | endif() 1617 | elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") 1618 | if (NOT _wholeTarget) 1619 | # for makefile based generator, we force the inclusion of the prefix header for a subset 1620 | # of the source files, if this is a multi-language target or has excluded files 1621 | set (_flags "") 1622 | cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) 1623 | cotire_add_pch_inclusion_flags( 1624 | "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" 1625 | "${_prefixFile}" "${_pchFile}" _flags) 1626 | set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") 1627 | # mark sources as cotired to prevent them from being used in another cotired target 1628 | set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") 1629 | endif() 1630 | # make source files depend on precompiled header 1631 | set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") 1632 | endif() 1633 | endfunction() 1634 | 1635 | function (cotire_get_first_set_property_value _propertyValueVar _type _object) 1636 | set (_properties ${ARGN}) 1637 | foreach (_property ${_properties}) 1638 | get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) 1639 | if (_propertyValue) 1640 | set (${_propertyValueVar} ${_propertyValue} PARENT_SCOPE) 1641 | return() 1642 | endif() 1643 | endforeach() 1644 | set (${_propertyValueVar} "" PARENT_SCOPE) 1645 | endfunction() 1646 | 1647 | function (cotire_setup_target_pch_usage _languages _targetSourceDir _target _wholeTarget) 1648 | if (MSVC) 1649 | # for Visual Studio, precompiled header inclusion is always done on the source file level 1650 | # see cotire_setup_prefix_file_inclusion 1651 | elseif (XCODE) 1652 | # for Xcode, we attach a pre-build action to generate the unity sources and prefix headers 1653 | # if necessary, we also generate a single prefix header which includes all language specific prefix headers 1654 | set (_prefixFiles "") 1655 | foreach (_language ${_languages}) 1656 | get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) 1657 | if (_prefixFile) 1658 | list (APPEND _prefixFiles "${_prefixFile}") 1659 | endif() 1660 | endforeach() 1661 | set (_cmds ${ARGN}) 1662 | list (LENGTH _prefixFiles _numberOfPrefixFiles) 1663 | if (_numberOfPrefixFiles GREATER 1) 1664 | cotire_make_prefix_file_path("" ${_target} _prefixHeader) 1665 | cotire_setup_combine_command("${_targetSourceDir}" "" "${_prefixHeader}" "${_prefixFiles}" _cmds) 1666 | else() 1667 | set (_prefixHeader "${_prefixFiles}") 1668 | endif() 1669 | if (COTIRE_DEBUG) 1670 | message (STATUS "add_custom_command: TARGET ${_target} PRE_BUILD ${_cmds}") 1671 | endif() 1672 | add_custom_command(TARGET "${_target}" 1673 | PRE_BUILD ${_cmds} 1674 | WORKING_DIRECTORY "${_targetSourceDir}" 1675 | COMMENT "Updating target ${_target} prefix headers" VERBATIM) 1676 | # make Xcode precompile the generated prefix header with ProcessPCH and ProcessPCH++ 1677 | set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES") 1678 | set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${_prefixHeader}") 1679 | elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") 1680 | if (_wholeTarget) 1681 | # for makefile based generator, we force inclusion of the prefix header for all target source files 1682 | # if this is a single-language target without any excluded files 1683 | set (_language "${_languages}") 1684 | get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) 1685 | get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER) 1686 | set (_flags "") 1687 | cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) 1688 | cotire_add_pch_inclusion_flags( 1689 | "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" 1690 | "${_prefixFile}" "${_pchFile}" _flags) 1691 | set_property (TARGET ${_target} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") 1692 | endif() 1693 | endif() 1694 | endfunction() 1695 | 1696 | function (cotire_setup_unity_generation_commands _language _targetSourceDir _target _targetScript _unityFiles _cmdsVar) 1697 | set (_dependencySources "") 1698 | cotire_get_unity_source_dependencies(${_language} ${_target} _dependencySources ${ARGN}) 1699 | foreach (_unityFile ${_unityFiles}) 1700 | file (RELATIVE_PATH _unityFileRelPath "${CMAKE_BINARY_DIR}" "${_unityFile}") 1701 | set_property (SOURCE "${_unityFile}" PROPERTY GENERATED TRUE) 1702 | # set up compiled unity source dependencies 1703 | # this ensures that missing source files are generated before the unity file is compiled 1704 | if (COTIRE_DEBUG AND _dependencySources) 1705 | message (STATUS "${_unityFile} OBJECT_DEPENDS ${_dependencySources}") 1706 | endif() 1707 | if (_dependencySources) 1708 | set_property (SOURCE "${_unityFile}" PROPERTY OBJECT_DEPENDS ${_dependencySources}) 1709 | endif() 1710 | if (MSVC) 1711 | # unity file compilation results in potentially huge object file, thus use /bigobj by default unter MSVC 1712 | set_property (SOURCE "${_unityFile}" APPEND_STRING PROPERTY COMPILE_FLAGS "/bigobj") 1713 | endif() 1714 | cotire_set_cmd_to_prologue(_unityCmd) 1715 | list (APPEND _unityCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "unity" "${_targetScript}" "${_unityFile}") 1716 | if (COTIRE_DEBUG) 1717 | message (STATUS "add_custom_command: OUTPUT ${_unityFile} COMMAND ${_unityCmd} DEPENDS ${_targetScript}") 1718 | endif() 1719 | add_custom_command( 1720 | OUTPUT "${_unityFile}" 1721 | COMMAND ${_unityCmd} 1722 | DEPENDS "${_targetScript}" 1723 | COMMENT "Generating ${_language} unity source ${_unityFileRelPath}" 1724 | WORKING_DIRECTORY "${_targetSourceDir}" VERBATIM) 1725 | list (APPEND ${_cmdsVar} COMMAND ${_unityCmd}) 1726 | endforeach() 1727 | set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) 1728 | endfunction() 1729 | 1730 | function (cotire_setup_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFiles _cmdsVar) 1731 | set (_sourceFiles ${ARGN}) 1732 | list (LENGTH _unityFiles _numberOfUnityFiles) 1733 | if (_numberOfUnityFiles GREATER 1) 1734 | # create a joint unity file from all unity file segments 1735 | cotire_make_untiy_source_file_paths(${_language} ${_target} 0 _unityFile ${_unityFiles}) 1736 | cotire_setup_combine_command("${_targetSourceDir}" "${_targetScript}" "${_unityFile}" "${_unityFiles}" ${_cmdsVar}) 1737 | else() 1738 | set (_unityFile "${_unityFiles}") 1739 | endif() 1740 | file (RELATIVE_PATH _prefixFileRelPath "${CMAKE_BINARY_DIR}" "${_prefixFile}") 1741 | set (_dependencySources "") 1742 | cotire_get_prefix_header_dependencies(${_language} ${_target} _dependencySources ${_sourceFiles}) 1743 | cotire_set_cmd_to_prologue(_prefixCmd) 1744 | list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "prefix" "${_targetScript}" "${_prefixFile}" "${_unityFile}") 1745 | set_property (SOURCE "${_prefixFile}" PROPERTY GENERATED TRUE) 1746 | if (COTIRE_DEBUG) 1747 | message (STATUS "add_custom_command: OUTPUT ${_prefixFile} COMMAND ${_prefixCmd} DEPENDS ${_targetScript} ${_unityFile} ${_dependencySources}") 1748 | endif() 1749 | add_custom_command( 1750 | OUTPUT "${_prefixFile}" 1751 | COMMAND ${_prefixCmd} 1752 | DEPENDS "${_targetScript}" "${_unityFile}" ${_dependencySources} 1753 | COMMENT "Generating ${_language} prefix header ${_prefixFileRelPath}" 1754 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) 1755 | list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) 1756 | set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) 1757 | endfunction() 1758 | 1759 | function (cotire_setup_combine_command _sourceDir _targetScript _joinedFile _files _cmdsVar) 1760 | set (_filesPaths "") 1761 | foreach (_file ${_files}) 1762 | if (IS_ABSOLUTE "${_file}") 1763 | set (_filePath "${_file}") 1764 | else() 1765 | get_filename_component(_filePath "${_sourceDir}/${_file}" ABSOLUTE) 1766 | endif() 1767 | file (RELATIVE_PATH _fileRelPath "${CMAKE_BINARY_DIR}" "${_filePath}") 1768 | if (NOT IS_ABSOLUTE "${_fileRelPath}" AND NOT "${_fileRelPath}" MATCHES "^\\.\\.") 1769 | list (APPEND _filesPaths "${_fileRelPath}") 1770 | else() 1771 | list (APPEND _filesPaths "${_filePath}") 1772 | endif() 1773 | endforeach() 1774 | cotire_set_cmd_to_prologue(_prefixCmd) 1775 | list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "combine") 1776 | if (_targetScript) 1777 | list (APPEND _prefixCmd "${_targetScript}") 1778 | endif() 1779 | list (APPEND _prefixCmd "${_joinedFile}" ${_filesPaths}) 1780 | if (COTIRE_DEBUG) 1781 | message (STATUS "add_custom_command: OUTPUT ${_joinedFile} COMMAND ${_prefixCmd} DEPENDS ${_files}") 1782 | endif() 1783 | set_property (SOURCE "${_joinedFile}" PROPERTY GENERATED TRUE) 1784 | file (RELATIVE_PATH _joinedFileRelPath "${CMAKE_BINARY_DIR}" "${_joinedFile}") 1785 | add_custom_command( 1786 | OUTPUT "${_joinedFile}" 1787 | COMMAND ${_prefixCmd} 1788 | DEPENDS ${_files} 1789 | COMMENT "Generating ${_joinedFileRelPath}" 1790 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) 1791 | list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) 1792 | set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) 1793 | endfunction() 1794 | 1795 | function (cotire_init_cotire_target_properties _target) 1796 | get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER SET) 1797 | if (NOT _isSet) 1798 | set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER TRUE) 1799 | endif() 1800 | get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD SET) 1801 | if (NOT _isSet) 1802 | set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD TRUE) 1803 | endif() 1804 | get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN SET) 1805 | if (NOT _isSet) 1806 | set_property(TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN FALSE) 1807 | endif() 1808 | get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH SET) 1809 | if (NOT _isSet) 1810 | set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_SOURCE_DIR}") 1811 | cotire_check_is_path_relative_to("${CMAKE_BINARY_DIR}" _isRelative "${CMAKE_SOURCE_DIR}") 1812 | if (NOT _isRelative) 1813 | set_property(TARGET ${_target} APPEND PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_BINARY_DIR}") 1814 | endif() 1815 | endif() 1816 | get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH SET) 1817 | if (NOT _isSet) 1818 | set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH "") 1819 | endif() 1820 | get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS SET) 1821 | if (NOT _isSet) 1822 | set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS "") 1823 | endif() 1824 | get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS SET) 1825 | if (NOT _isSet) 1826 | set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS "") 1827 | endif() 1828 | get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES SET) 1829 | if (NOT _isSet) 1830 | if (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) 1831 | set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}") 1832 | else() 1833 | set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "") 1834 | endif() 1835 | endif() 1836 | endfunction() 1837 | 1838 | function (cotire_make_target_message _target _languages _disableMsg _targetMsgVar) 1839 | get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) 1840 | get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) 1841 | string (REPLACE ";" " " _languagesStr "${_languages}") 1842 | string (REPLACE ";" ", " _excludedStr "${ARGN}") 1843 | set (_targetMsg "") 1844 | if (NOT _languages) 1845 | set (_targetMsg "Target ${_target} cannot be cotired.") 1846 | if (_disableMsg) 1847 | set (_targetMsg "${_targetMsg} ${_disableMsg}") 1848 | endif() 1849 | elseif (NOT _targetUsePCH AND NOT _targetAddSCU) 1850 | set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build and precompiled header.") 1851 | if (_disableMsg) 1852 | set (_targetMsg "${_targetMsg} ${_disableMsg}") 1853 | endif() 1854 | elseif (NOT _targetUsePCH) 1855 | if (_allExcludedSourceFiles) 1856 | set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without precompiled header.") 1857 | else() 1858 | set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header.") 1859 | endif() 1860 | if (_disableMsg) 1861 | set (_targetMsg "${_targetMsg} ${_disableMsg}") 1862 | endif() 1863 | elseif (NOT _targetAddSCU) 1864 | if (_allExcludedSourceFiles) 1865 | set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without unity build.") 1866 | else() 1867 | set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build.") 1868 | endif() 1869 | else() 1870 | if (_allExcludedSourceFiles) 1871 | set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr}.") 1872 | else() 1873 | set (_targetMsg "${_languagesStr} target ${_target} cotired.") 1874 | endif() 1875 | endif() 1876 | set (${_targetMsgVar} "${_targetMsg}" PARENT_SCOPE) 1877 | endfunction() 1878 | 1879 | function (cotire_choose_target_languages _targetSourceDir _target _targetLanguagesVar) 1880 | set (_languages ${ARGN}) 1881 | set (_allSourceFiles "") 1882 | set (_allExcludedSourceFiles "") 1883 | set (_allCotiredSourceFiles "") 1884 | set (_targetLanguages "") 1885 | get_target_property(_targetType ${_target} TYPE) 1886 | get_target_property(_targetSourceFiles ${_target} SOURCES) 1887 | get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) 1888 | get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) 1889 | set (_disableMsg "") 1890 | foreach (_language ${_languages}) 1891 | get_target_property(_prefixHeader ${_target} COTIRE_${_language}_PREFIX_HEADER) 1892 | get_target_property(_unityBuildFile ${_target} COTIRE_${_language}_UNITY_SOURCE) 1893 | if (_prefixHeader OR _unityBuildFile) 1894 | message (WARNING "Target ${_target} has already been cotired.") 1895 | set (${_targetLanguagesVar} "" PARENT_SCOPE) 1896 | return() 1897 | endif() 1898 | if (_targetUsePCH AND "${_language}" STREQUAL "C" OR "${_language}" STREQUAL "CXX") 1899 | cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _disableMsg) 1900 | if (_disableMsg) 1901 | set (_targetUsePCH FALSE) 1902 | endif() 1903 | endif() 1904 | set (_sourceFiles "") 1905 | set (_excludedSources "") 1906 | set (_cotiredSources "") 1907 | cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) 1908 | if (_sourceFiles OR _excludedSources OR _cotiredSources) 1909 | list (APPEND _targetLanguages ${_language}) 1910 | endif() 1911 | if (_sourceFiles) 1912 | list (APPEND _allSourceFiles ${_sourceFiles}) 1913 | endif() 1914 | if (_excludedSources) 1915 | list (APPEND _allExcludedSourceFiles ${_excludedSources}) 1916 | endif() 1917 | if (_cotiredSources) 1918 | list (APPEND _allCotiredSourceFiles ${_cotiredSources}) 1919 | endif() 1920 | endforeach() 1921 | set (_targetMsgLevel STATUS) 1922 | if (NOT _targetLanguages) 1923 | string (REPLACE ";" " or " _languagesStr "${_languages}") 1924 | set (_disableMsg "No ${_languagesStr} source files.") 1925 | set (_targetUsePCH FALSE) 1926 | set (_targetAddSCU FALSE) 1927 | endif() 1928 | if (_targetUsePCH) 1929 | list (LENGTH _allSourceFiles _numberOfSources) 1930 | if (_numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) 1931 | set (_disableMsg "Too few applicable sources.") 1932 | set (_targetUsePCH FALSE) 1933 | elseif (_allCotiredSourceFiles) 1934 | cotire_get_source_file_property_values(_cotireTargets COTIRE_TARGET ${_allCotiredSourceFiles}) 1935 | list (REMOVE_DUPLICATES _cotireTargets) 1936 | string (REPLACE ";" ", " _cotireTargetsStr "${_cotireTargets}") 1937 | set (_disableMsg "Target sources already include a precompiled header for target(s) ${_cotireTargets}.") 1938 | set (_disableMsg "${_disableMsg} Set target property COTIRE_ENABLE_PRECOMPILED_HEADER to FALSE for targets ${_target},") 1939 | set (_disableMsg "${_disableMsg} ${_cotireTargetsStr} to get a workable build system.") 1940 | set (_targetMsgLevel SEND_ERROR) 1941 | set (_targetUsePCH FALSE) 1942 | elseif (XCODE AND _allExcludedSourceFiles) 1943 | # for Xcode, we cannot apply the precompiled header to individual sources, only to the whole target 1944 | set (_disableMsg "Exclusion of source files not supported for generator Xcode.") 1945 | set (_targetUsePCH FALSE) 1946 | elseif (XCODE AND "${_targetType}" STREQUAL "OBJECT_LIBRARY") 1947 | # for Xcode, we cannot apply the required PRE_BUILD action to generate the prefix header to an OBJECT_LIBRARY target 1948 | set (_disableMsg "Required PRE_BUILD action not supported for OBJECT_LIBRARY targets for generator Xcode.") 1949 | set (_targetUsePCH FALSE) 1950 | endif() 1951 | endif() 1952 | set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER ${_targetUsePCH}) 1953 | set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD ${_targetAddSCU}) 1954 | cotire_make_target_message(${_target} "${_targetLanguages}" "${_disableMsg}" _targetMsg ${_allExcludedSourceFiles}) 1955 | if (_targetMsg) 1956 | if (NOT DEFINED COTIREMSG_${_target}) 1957 | set (COTIREMSG_${_target} "") 1958 | endif() 1959 | if (COTIRE_VERBOSE OR NOT "${_targetMsgLevel}" STREQUAL "STATUS" OR 1960 | NOT "${COTIREMSG_${_target}}" STREQUAL "${_targetMsg}") 1961 | # cache message to avoid redundant messages on re-configure 1962 | set (COTIREMSG_${_target} "${_targetMsg}" CACHE INTERNAL "${_target} cotire message.") 1963 | message (${_targetMsgLevel} "${_targetMsg}") 1964 | endif() 1965 | endif() 1966 | set (${_targetLanguagesVar} ${_targetLanguages} PARENT_SCOPE) 1967 | endfunction() 1968 | 1969 | function (cotire_process_target_language _language _configurations _targetSourceDir _targetBinaryDir _target _wholeTargetVar _cmdsVar) 1970 | set (${_cmdsVar} "" PARENT_SCOPE) 1971 | get_target_property(_targetSourceFiles ${_target} SOURCES) 1972 | set (_sourceFiles "") 1973 | set (_excludedSources "") 1974 | set (_cotiredSources "") 1975 | cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) 1976 | if (NOT _sourceFiles) 1977 | return() 1978 | endif() 1979 | set (_wholeTarget ${${_wholeTargetVar}}) 1980 | set (_cmds "") 1981 | # check for user provided unity source file list 1982 | get_property(_unitySourceFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE_INIT) 1983 | if (NOT _unitySourceFiles) 1984 | set (_unitySourceFiles ${_sourceFiles} ${_cotiredSources}) 1985 | endif() 1986 | cotire_generate_target_script( 1987 | ${_language} "${_configurations}" "${_targetSourceDir}" "${_targetBinaryDir}" ${_target} _targetScript ${_unitySourceFiles}) 1988 | get_target_property(_maxIncludes ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) 1989 | if (NOT _maxIncludes) 1990 | set (_maxIncludes 0) 1991 | endif() 1992 | cotire_make_untiy_source_file_paths(${_language} ${_target} ${_maxIncludes} _unityFiles ${_unitySourceFiles}) 1993 | if (NOT _unityFiles) 1994 | return() 1995 | endif() 1996 | cotire_setup_unity_generation_commands( 1997 | ${_language} "${_targetSourceDir}" ${_target} "${_targetScript}" "${_unityFiles}" _cmds ${_unitySourceFiles}) 1998 | cotire_make_prefix_file_path(${_language} ${_target} _prefixFile) 1999 | if (_prefixFile) 2000 | # check for user provided prefix header files 2001 | get_property(_prefixHeaderFiles TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT) 2002 | if (_prefixHeaderFiles) 2003 | cotire_setup_combine_command("${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_prefixHeaderFiles}" _cmds) 2004 | else() 2005 | cotire_setup_prefix_generation_command( 2006 | ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_unityFiles}" _cmds ${_unitySourceFiles}) 2007 | endif() 2008 | get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) 2009 | if (_targetUsePCH) 2010 | cotire_make_pch_file_path(${_language} "${_targetSourceDir}" ${_target} _pchFile) 2011 | if (_pchFile) 2012 | cotire_setup_pch_file_compilation( 2013 | ${_language} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) 2014 | if (_excludedSources) 2015 | set (_wholeTarget FALSE) 2016 | endif() 2017 | cotire_setup_prefix_file_inclusion( 2018 | ${_language} ${_target} ${_wholeTarget} "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) 2019 | endif() 2020 | endif() 2021 | endif() 2022 | # mark target as cotired for language 2023 | set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE "${_unityFiles}") 2024 | if (_prefixFile) 2025 | set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER "${_prefixFile}") 2026 | if (_targetUsePCH AND _pchFile) 2027 | set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER "${_pchFile}") 2028 | endif() 2029 | endif() 2030 | set (${_wholeTargetVar} ${_wholeTarget} PARENT_SCOPE) 2031 | set (${_cmdsVar} ${_cmds} PARENT_SCOPE) 2032 | endfunction() 2033 | 2034 | function (cotire_setup_clean_target _target) 2035 | set (_cleanTargetName "${_target}${COTIRE_CLEAN_TARGET_SUFFIX}") 2036 | if (NOT TARGET "${_cleanTargetName}") 2037 | cotire_set_cmd_to_prologue(_cmds) 2038 | get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" ABSOLUTE) 2039 | list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${_outputDir}" "${COTIRE_INTDIR}" "${_target}") 2040 | add_custom_target(${_cleanTargetName} COMMAND ${_cmds} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" 2041 | COMMENT "Cleaning up target ${_target} cotire generated files" VERBATIM) 2042 | cotire_init_target("${_cleanTargetName}") 2043 | endif() 2044 | endfunction() 2045 | 2046 | function (cotire_setup_pch_target _languages _configurations _target) 2047 | if ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") 2048 | # for makefile based generators, we add a custom target to trigger the generation of the cotire related files 2049 | set (_dependsFiles "") 2050 | foreach (_language ${_languages}) 2051 | set (_props COTIRE_${_language}_PREFIX_HEADER COTIRE_${_language}_UNITY_SOURCE) 2052 | if (NOT MSVC) 2053 | # Visual Studio only creates precompiled header as a side effect 2054 | list(INSERT _props 0 COTIRE_${_language}_PRECOMPILED_HEADER) 2055 | endif() 2056 | cotire_get_first_set_property_value(_dependsFile TARGET ${_target} ${_props}) 2057 | if (_dependsFile) 2058 | list (APPEND _dependsFiles "${_dependsFile}") 2059 | endif() 2060 | endforeach() 2061 | if (_dependsFiles) 2062 | set (_pchTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}") 2063 | add_custom_target("${_pchTargetName}" DEPENDS ${_dependsFiles}) 2064 | cotire_init_target("${_pchTargetName}") 2065 | cotire_add_to_pch_all_target(${_pchTargetName}) 2066 | endif() 2067 | else() 2068 | # for other generators, we add the "clean all" target to clean up the precompiled header 2069 | cotire_setup_clean_all_target() 2070 | endif() 2071 | endfunction() 2072 | 2073 | function (cotire_setup_unity_build_target _languages _configurations _targetSourceDir _target) 2074 | get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME) 2075 | if (NOT _unityTargetName) 2076 | set (_unityTargetName "${_target}${COTIRE_UNITY_BUILD_TARGET_SUFFIX}") 2077 | endif() 2078 | # determine unity target sub type 2079 | get_target_property(_targetType ${_target} TYPE) 2080 | if ("${_targetType}" STREQUAL "EXECUTABLE") 2081 | get_target_property(_isWin32 ${_target} WIN32_EXECUTABLE) 2082 | get_target_property(_isMacOSX_Bundle ${_target} MACOSX_BUNDLE) 2083 | if (_isWin32) 2084 | set (_unityTargetSubType WIN32) 2085 | elseif (_isMacOSX_Bundle) 2086 | set (_unityTargetSubType MACOSX_BUNDLE) 2087 | else() 2088 | set (_unityTargetSubType "") 2089 | endif() 2090 | elseif (_targetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") 2091 | set (_unityTargetSubType "${CMAKE_MATCH_1}") 2092 | else() 2093 | message (WARNING "Unknown target type ${_targetType}.") 2094 | return() 2095 | endif() 2096 | # determine unity target sources 2097 | get_target_property(_targetSourceFiles ${_target} SOURCES) 2098 | set (_unityTargetSources ${_targetSourceFiles}) 2099 | get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) 2100 | foreach (_language ${_languages}) 2101 | get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE) 2102 | if (_unityFiles) 2103 | # remove source files that are included in the unity source 2104 | set (_sourceFiles "") 2105 | set (_excludedSources "") 2106 | set (_cotiredSources "") 2107 | cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) 2108 | if (_sourceFiles OR _cotiredSources) 2109 | list (REMOVE_ITEM _unityTargetSources ${_sourceFiles} ${_cotiredSources}) 2110 | endif() 2111 | # if cotire is applied to a target which has not been added in the current source dir, 2112 | # non-existing files cannot be referenced from the unity build target (this is a CMake restriction) 2113 | if (NOT "${_targetSourceDir}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") 2114 | set (_nonExistingFiles "") 2115 | foreach (_file ${_unityTargetSources}) 2116 | if (NOT EXISTS "${_file}") 2117 | list (APPEND _nonExistingFiles "${_file}") 2118 | endif() 2119 | endforeach() 2120 | if (_nonExistingFiles) 2121 | if (COTIRE_VERBOSE) 2122 | message (STATUS "removing non-existing ${_nonExistingFiles} from ${_unityTargetName}") 2123 | endif() 2124 | list (REMOVE_ITEM _unityTargetSources ${_nonExistingFiles}) 2125 | endif() 2126 | endif() 2127 | # add unity source files instead 2128 | list (APPEND _unityTargetSources ${_unityFiles}) 2129 | # make unity files use precompiled header if there are multiple unity files 2130 | list (LENGTH _unityFiles _numberOfUnityFiles) 2131 | if (_targetUsePCH AND _numberOfUnityFiles GREATER ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) 2132 | get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) 2133 | get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER) 2134 | if (_prefixFile AND _pchFile) 2135 | cotire_setup_pch_file_compilation( 2136 | ${_language} "${_targetSourceDir}" "" "${_prefixFile}" "${_pchFile}" ${_unityFiles}) 2137 | cotire_setup_prefix_file_inclusion( 2138 | ${_language} ${_target} FALSE "${_prefixFile}" "${_pchFile}" ${_unityFiles}) 2139 | # add the prefix header to unity target sources 2140 | list (APPEND _unityTargetSources "${_prefixFile}") 2141 | endif() 2142 | endif() 2143 | endif() 2144 | endforeach() 2145 | if (COTIRE_DEBUG) 2146 | message (STATUS "add ${_targetType} ${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}") 2147 | endif() 2148 | # generate unity target 2149 | if ("${_targetType}" STREQUAL "EXECUTABLE") 2150 | add_executable(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) 2151 | else() 2152 | add_library(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) 2153 | endif() 2154 | set (_outputDirProperties 2155 | ARCHIVE_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY_ 2156 | LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY_ 2157 | RUNTIME_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY_) 2158 | # copy output location properties 2159 | if (COTIRE_UNITY_OUTPUT_DIRECTORY) 2160 | set (_setDefaultOutputDir TRUE) 2161 | if (IS_ABSOLUTE "${COTIRE_UNITY_OUTPUT_DIRECTORY}") 2162 | set (_outputDir "${COTIRE_UNITY_OUTPUT_DIRECTORY}") 2163 | else() 2164 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties}) 2165 | cotrie_resolve_config_properites("${_configurations}" _properties ${_outputDirProperties}) 2166 | foreach (_property ${_properties}) 2167 | get_property(_outputDir TARGET ${_target} PROPERTY ${_property}) 2168 | if (_outputDir) 2169 | get_filename_component(_outputDir "${_outputDir}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) 2170 | set_property(TARGET ${_target} PROPERTY ${_property} "${_outputDir}") 2171 | set (_setDefaultOutputDir FALSE) 2172 | endif() 2173 | endforeach() 2174 | if (_setDefaultOutputDir) 2175 | get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) 2176 | endif() 2177 | endif() 2178 | if (_setDefaultOutputDir) 2179 | set_target_properties(${_unityTargetName} PROPERTIES 2180 | ARCHIVE_OUTPUT_DIRECTORY "${_outputDir}" 2181 | LIBRARY_OUTPUT_DIRECTORY "${_outputDir}" 2182 | RUNTIME_OUTPUT_DIRECTORY "${_outputDir}") 2183 | endif() 2184 | else() 2185 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties}) 2186 | endif() 2187 | # copy output name 2188 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} 2189 | ARCHIVE_OUTPUT_NAME ARCHIVE_OUTPUT_NAME_ 2190 | LIBRARY_OUTPUT_NAME LIBRARY_OUTPUT_NAME_ 2191 | OUTPUT_NAME OUTPUT_NAME_ 2192 | RUNTIME_OUTPUT_NAME RUNTIME_OUTPUT_NAME_ 2193 | PREFIX _POSTFIX SUFFIX) 2194 | # copy compile stuff 2195 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} 2196 | COMPILE_DEFINITIONS COMPILE_DEFINITIONS_ 2197 | COMPILE_FLAGS Fortran_FORMAT 2198 | INCLUDE_DIRECTORIES 2199 | INTERPROCEDURAL_OPTIMIZATION INTERPROCEDURAL_OPTIMIZATION_ 2200 | POSITION_INDEPENDENT_CODE) 2201 | # copy link stuff 2202 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} 2203 | BUILD_WITH_INSTALL_RPATH INSTALL_RPATH INSTALL_RPATH_USE_LINK_PATH SKIP_BUILD_RPATH 2204 | LINKER_LANGUAGE LINK_DEPENDS 2205 | LINK_FLAGS LINK_FLAGS_ 2206 | LINK_INTERFACE_LIBRARIES LINK_INTERFACE_LIBRARIES_ 2207 | LINK_INTERFACE_MULTIPLICITY LINK_INTERFACE_MULTIPLICITY_ 2208 | LINK_SEARCH_START_STATIC LINK_SEARCH_END_STATIC 2209 | STATIC_LIBRARY_FLAGS STATIC_LIBRARY_FLAGS_ 2210 | NO_SONAME SOVERSION VERSION) 2211 | # copy Qt stuff 2212 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} 2213 | AUTOMOC AUTOMOC_MOC_OPTIONS) 2214 | # copy cmake stuff 2215 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} 2216 | IMPLICIT_DEPENDS_INCLUDE_TRANSFORM RULE_LAUNCH_COMPILE RULE_LAUNCH_CUSTOM RULE_LAUNCH_LINK) 2217 | # copy platform stuff 2218 | if (APPLE) 2219 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} 2220 | BUNDLE BUNDLE_EXTENSION FRAMEWORK INSTALL_NAME_DIR MACOSX_BUNDLE_INFO_PLIST MACOSX_FRAMEWORK_INFO_PLIST 2221 | OSX_ARCHITECTURES OSX_ARCHITECTURES_ PRIVATE_HEADER PUBLIC_HEADER RESOURCE) 2222 | elseif (WIN32) 2223 | cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} 2224 | GNUtoMS 2225 | VS_DOTNET_REFERENCES VS_GLOBAL_KEYWORD VS_GLOBAL_PROJECT_TYPES VS_KEYWORD 2226 | VS_SCC_AUXPATH VS_SCC_LOCALPATH VS_SCC_PROJECTNAME VS_SCC_PROVIDER 2227 | VS_WINRT_EXTENSIONS VS_WINRT_REFERENCES) 2228 | endif() 2229 | # use output name from original target 2230 | get_target_property(_targetOutputName ${_unityTargetName} OUTPUT_NAME) 2231 | if (NOT _targetOutputName) 2232 | set_property(TARGET ${_unityTargetName} PROPERTY OUTPUT_NAME "${_target}") 2233 | endif() 2234 | # use export symbol from original target 2235 | cotire_get_target_export_symbol("${_target}" _defineSymbol) 2236 | if (_defineSymbol) 2237 | set_property(TARGET ${_unityTargetName} PROPERTY DEFINE_SYMBOL "${_defineSymbol}") 2238 | if ("${_targetType}" STREQUAL "EXECUTABLE") 2239 | set_property(TARGET ${_unityTargetName} PROPERTY ENABLE_EXPORTS TRUE) 2240 | endif() 2241 | endif() 2242 | cotire_init_target(${_unityTargetName}) 2243 | cotire_add_to_unity_all_target(${_unityTargetName}) 2244 | set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_TARGET_NAME "${_unityTargetName}") 2245 | endfunction(cotire_setup_unity_build_target) 2246 | 2247 | function (cotire_target _target) 2248 | set(_options "") 2249 | set(_oneValueArgs SOURCE_DIR BINARY_DIR) 2250 | set(_multiValueArgs LANGUAGES CONFIGURATIONS) 2251 | cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) 2252 | if (NOT _option_SOURCE_DIR) 2253 | set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 2254 | endif() 2255 | if (NOT _option_BINARY_DIR) 2256 | set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") 2257 | endif() 2258 | if (NOT _option_LANGUAGES) 2259 | get_property (_option_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 2260 | endif() 2261 | if (NOT _option_CONFIGURATIONS) 2262 | if (CMAKE_CONFIGURATION_TYPES) 2263 | set (_option_CONFIGURATIONS ${CMAKE_CONFIGURATION_TYPES}) 2264 | elseif (CMAKE_BUILD_TYPE) 2265 | set (_option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}") 2266 | else() 2267 | set (_option_CONFIGURATIONS "None") 2268 | endif() 2269 | endif() 2270 | # trivial checks 2271 | get_target_property(_imported ${_target} IMPORTED) 2272 | if (_imported) 2273 | message (WARNING "Imported target ${_target} cannot be cotired.") 2274 | return() 2275 | endif() 2276 | # check if target needs to be cotired for build type 2277 | # when using configuration types, the test is performed at build time 2278 | cotire_init_cotire_target_properties(${_target}) 2279 | if (NOT CMAKE_CONFIGURATION_TYPES) 2280 | if (CMAKE_BUILD_TYPE) 2281 | list (FIND _option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}" _index) 2282 | else() 2283 | list (FIND _option_CONFIGURATIONS "None" _index) 2284 | endif() 2285 | if (_index EQUAL -1) 2286 | if (COTIRE_DEBUG) 2287 | message (STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} not cotired (${_option_CONFIGURATIONS})") 2288 | endif() 2289 | return() 2290 | endif() 2291 | endif() 2292 | # choose languages that apply to the target 2293 | cotire_choose_target_languages("${_option_SOURCE_DIR}" "${_target}" _targetLanguages ${_option_LANGUAGES}) 2294 | if (NOT _targetLanguages) 2295 | return() 2296 | endif() 2297 | list (LENGTH _targetLanguages _numberOfLanguages) 2298 | if (_numberOfLanguages GREATER 1) 2299 | set (_wholeTarget FALSE) 2300 | else() 2301 | set (_wholeTarget TRUE) 2302 | endif() 2303 | set (_cmds "") 2304 | foreach (_language ${_targetLanguages}) 2305 | cotire_process_target_language("${_language}" "${_option_CONFIGURATIONS}" 2306 | "${_option_SOURCE_DIR}" "${_option_BINARY_DIR}" ${_target} _wholeTarget _cmd) 2307 | if (_cmd) 2308 | list (APPEND _cmds ${_cmd}) 2309 | endif() 2310 | endforeach() 2311 | get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) 2312 | if (_targetAddSCU) 2313 | cotire_setup_unity_build_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" "${_option_SOURCE_DIR}" ${_target}) 2314 | endif() 2315 | get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) 2316 | if (_targetUsePCH) 2317 | cotire_setup_target_pch_usage("${_targetLanguages}" "${_option_SOURCE_DIR}" ${_target} ${_wholeTarget} ${_cmds}) 2318 | cotire_setup_pch_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target}) 2319 | endif() 2320 | get_target_property(_targetAddCleanTarget ${_target} COTIRE_ADD_CLEAN) 2321 | if (_targetAddCleanTarget) 2322 | cotire_setup_clean_target(${_target}) 2323 | endif() 2324 | endfunction() 2325 | 2326 | function (cotire_cleanup _binaryDir _cotireIntermediateDirName _targetName) 2327 | if (_targetName) 2328 | file (GLOB_RECURSE _cotireFiles "${_binaryDir}/${_targetName}*.*") 2329 | else() 2330 | file (GLOB_RECURSE _cotireFiles "${_binaryDir}/*.*") 2331 | endif() 2332 | # filter files in intermediate directory 2333 | set (_filesToRemove "") 2334 | foreach (_file ${_cotireFiles}) 2335 | get_filename_component(_dir "${_file}" PATH) 2336 | get_filename_component(_dirName "${_dir}" NAME) 2337 | if ("${_dirName}" STREQUAL "${_cotireIntermediateDirName}") 2338 | list (APPEND _filesToRemove "${_file}") 2339 | endif() 2340 | endforeach() 2341 | if (_filesToRemove) 2342 | if (COTIRE_VERBOSE) 2343 | message (STATUS "removing ${_filesToRemove}") 2344 | endif() 2345 | file (REMOVE ${_filesToRemove}) 2346 | endif() 2347 | endfunction() 2348 | 2349 | function (cotire_init_target _targetName) 2350 | if (COTIRE_TARGETS_FOLDER) 2351 | set_target_properties(${_targetName} PROPERTIES FOLDER "${COTIRE_TARGETS_FOLDER}") 2352 | endif() 2353 | if (MSVC_IDE) 2354 | set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE) 2355 | endif() 2356 | endfunction() 2357 | 2358 | function (cotire_add_to_pch_all_target _pchTargetName) 2359 | set (_targetName "${COTIRE_PCH_ALL_TARGET_NAME}") 2360 | if (NOT TARGET "${_targetName}") 2361 | add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) 2362 | cotire_init_target("${_targetName}") 2363 | endif() 2364 | cotire_setup_clean_all_target() 2365 | add_dependencies(${_targetName} ${_pchTargetName}) 2366 | endfunction() 2367 | 2368 | function (cotire_add_to_unity_all_target _unityTargetName) 2369 | set (_targetName "${COTIRE_UNITY_BUILD_ALL_TARGET_NAME}") 2370 | if (NOT TARGET "${_targetName}") 2371 | add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) 2372 | cotire_init_target("${_targetName}") 2373 | endif() 2374 | cotire_setup_clean_all_target() 2375 | add_dependencies(${_targetName} ${_unityTargetName}) 2376 | endfunction() 2377 | 2378 | function (cotire_setup_clean_all_target) 2379 | set (_targetName "${COTIRE_CLEAN_ALL_TARGET_NAME}") 2380 | if (NOT TARGET "${_targetName}") 2381 | cotire_set_cmd_to_prologue(_cmds) 2382 | list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${CMAKE_BINARY_DIR}" "${COTIRE_INTDIR}") 2383 | add_custom_target(${_targetName} COMMAND ${_cmds} 2384 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" COMMENT "Cleaning up all cotire generated files" VERBATIM) 2385 | cotire_init_target("${_targetName}") 2386 | endif() 2387 | endfunction() 2388 | 2389 | function (cotire) 2390 | set(_options "") 2391 | set(_oneValueArgs SOURCE_DIR BINARY_DIR) 2392 | set(_multiValueArgs LANGUAGES CONFIGURATIONS) 2393 | cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) 2394 | set (_targets ${_option_UNPARSED_ARGUMENTS}) 2395 | if (NOT _option_SOURCE_DIR) 2396 | set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 2397 | endif() 2398 | if (NOT _option_BINARY_DIR) 2399 | set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") 2400 | endif() 2401 | foreach (_target ${_targets}) 2402 | if (TARGET ${_target}) 2403 | cotire_target(${_target} LANGUAGES ${_option_LANGUAGES} CONFIGURATIONS ${_option_CONFIGURATIONS} 2404 | SOURCE_DIR "${_option_SOURCE_DIR}" BINARY_DIR "${_option_BINARY_DIR}") 2405 | else() 2406 | message (WARNING "${_target} is not a target") 2407 | endif() 2408 | endforeach() 2409 | endfunction() 2410 | 2411 | if (CMAKE_SCRIPT_MODE_FILE) 2412 | 2413 | # cotire is being run in script mode 2414 | # locate -P on command args 2415 | set (COTIRE_ARGC -1) 2416 | foreach (_index RANGE ${CMAKE_ARGC}) 2417 | if (COTIRE_ARGC GREATER -1) 2418 | set (COTIRE_ARGV${COTIRE_ARGC} "${CMAKE_ARGV${_index}}") 2419 | math (EXPR COTIRE_ARGC "${COTIRE_ARGC} + 1") 2420 | elseif ("${CMAKE_ARGV${_index}}" STREQUAL "-P") 2421 | set (COTIRE_ARGC 0) 2422 | endif() 2423 | endforeach() 2424 | 2425 | # include target script if available 2426 | if ("${COTIRE_ARGV2}" MATCHES "\\.cmake$") 2427 | include("${COTIRE_ARGV2}") 2428 | endif() 2429 | 2430 | if (COTIRE_DEBUG) 2431 | message (STATUS "${COTIRE_ARGV0} ${COTIRE_ARGV1} ${COTIRE_ARGV2} ${COTIRE_ARGV3} ${COTIRE_ARGV4} ${COTIRE_ARGV5}") 2432 | endif() 2433 | 2434 | if (WIN32) 2435 | # for MSVC, compiler IDs may not always be set correctly 2436 | if (MSVC) 2437 | set (CMAKE_C_COMPILER_ID "MSVC") 2438 | set (CMAKE_CXX_COMPILER_ID "MSVC") 2439 | endif() 2440 | endif() 2441 | 2442 | if (NOT COTIRE_BUILD_TYPE) 2443 | set (COTIRE_BUILD_TYPE "None") 2444 | endif() 2445 | string (TOUPPER "${COTIRE_BUILD_TYPE}" _upperConfig) 2446 | set (_includeDirs ${COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}}) 2447 | set (_compileDefinitions ${COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}}) 2448 | set (_compileFlags ${COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}}) 2449 | # check if target has been cotired for actual build type COTIRE_BUILD_TYPE 2450 | list (FIND COTIRE_TARGET_CONFIGURATION_TYPES "${COTIRE_BUILD_TYPE}" _index) 2451 | if (_index GREATER -1) 2452 | set (_sources ${COTIRE_TARGET_SOURCES}) 2453 | set (_sourcesDefinitions ${COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig}}) 2454 | else() 2455 | if (COTIRE_DEBUG) 2456 | message (STATUS "COTIRE_BUILD_TYPE=${COTIRE_BUILD_TYPE} not cotired (${COTIRE_TARGET_CONFIGURATION_TYPES})") 2457 | endif() 2458 | set (_sources "") 2459 | set (_sourcesDefinitions "") 2460 | endif() 2461 | set (_targetPreUndefs ${COTIRE_TARGET_PRE_UNDEFS}) 2462 | set (_targetPostUndefs ${COTIRE_TARGET_POST_UNDEFS}) 2463 | set (_sourcesPreUndefs ${COTIRE_TARGET_SOURCES_PRE_UNDEFS}) 2464 | set (_sourcesPostUndefs ${COTIRE_TARGET_SOURCES_POST_UNDEFS}) 2465 | 2466 | if ("${COTIRE_ARGV1}" STREQUAL "unity") 2467 | 2468 | cotire_select_unity_source_files("${COTIRE_ARGV3}" _sources ${_sources}) 2469 | cotire_generate_unity_source( 2470 | "${COTIRE_ARGV3}" ${_sources} 2471 | LANGUAGE "${COTIRE_TARGET_LANGUAGE}" 2472 | DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV2}" 2473 | SOURCES_COMPILE_DEFINITIONS ${_sourcesDefinitions} 2474 | PRE_UNDEFS ${_targetPreUndefs} 2475 | POST_UNDEFS ${_targetPostUndefs} 2476 | SOURCES_PRE_UNDEFS ${_sourcesPreUndefs} 2477 | SOURCES_POST_UNDEFS ${_sourcesPostUndefs}) 2478 | 2479 | elseif ("${COTIRE_ARGV1}" STREQUAL "prefix") 2480 | 2481 | set (_files "") 2482 | foreach (_index RANGE 4 ${COTIRE_ARGC}) 2483 | if (COTIRE_ARGV${_index}) 2484 | list (APPEND _files "${COTIRE_ARGV${_index}}") 2485 | endif() 2486 | endforeach() 2487 | 2488 | cotire_generate_prefix_header( 2489 | "${COTIRE_ARGV3}" ${_files} 2490 | COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" 2491 | COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} 2492 | COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" 2493 | COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" 2494 | LANGUAGE "${COTIRE_TARGET_LANGUAGE}" 2495 | DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV4}" ${COTIRE_TARGET_PREFIX_DEPENDS} 2496 | IGNORE_PATH "${COTIRE_TARGET_IGNORE_PATH};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH}" 2497 | INCLUDE_PATH ${COTIRE_TARGET_INCLUDE_PATH} 2498 | IGNORE_EXTENSIONS "${CMAKE_${COTIRE_TARGET_LANGUAGE}_SOURCE_FILE_EXTENSIONS};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS}" 2499 | INCLUDE_DIRECTORIES ${_includeDirs} 2500 | COMPILE_DEFINITIONS ${_compileDefinitions} 2501 | COMPILE_FLAGS ${_compileFlags}) 2502 | 2503 | elseif ("${COTIRE_ARGV1}" STREQUAL "precompile") 2504 | 2505 | set (_files "") 2506 | foreach (_index RANGE 5 ${COTIRE_ARGC}) 2507 | if (COTIRE_ARGV${_index}) 2508 | list (APPEND _files "${COTIRE_ARGV${_index}}") 2509 | endif() 2510 | endforeach() 2511 | 2512 | cotire_precompile_prefix_header( 2513 | "${COTIRE_ARGV3}" "${COTIRE_ARGV4}" "${COTIRE_ARGV5}" 2514 | COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" 2515 | COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} 2516 | COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" 2517 | COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" 2518 | LANGUAGE "${COTIRE_TARGET_LANGUAGE}" 2519 | INCLUDE_DIRECTORIES ${_includeDirs} 2520 | COMPILE_DEFINITIONS ${_compileDefinitions} 2521 | COMPILE_FLAGS ${_compileFlags}) 2522 | 2523 | elseif ("${COTIRE_ARGV1}" STREQUAL "combine") 2524 | 2525 | if (COTIRE_TARGET_LANGUAGE) 2526 | set (_startIndex 3) 2527 | else() 2528 | set (_startIndex 2) 2529 | endif() 2530 | set (_files "") 2531 | foreach (_index RANGE ${_startIndex} ${COTIRE_ARGC}) 2532 | if (COTIRE_ARGV${_index}) 2533 | list (APPEND _files "${COTIRE_ARGV${_index}}") 2534 | endif() 2535 | endforeach() 2536 | if (COTIRE_TARGET_LANGUAGE) 2537 | cotire_generate_unity_source(${_files} LANGUAGE "${COTIRE_TARGET_LANGUAGE}") 2538 | else() 2539 | cotire_generate_unity_source(${_files}) 2540 | endif() 2541 | 2542 | elseif ("${COTIRE_ARGV1}" STREQUAL "cleanup") 2543 | 2544 | cotire_cleanup("${COTIRE_ARGV2}" "${COTIRE_ARGV3}" "${COTIRE_ARGV4}") 2545 | 2546 | else() 2547 | message (FATAL_ERROR "Unknown cotire command \"${COTIRE_ARGV1}\".") 2548 | endif() 2549 | 2550 | else() 2551 | 2552 | # cotire is being run in include mode 2553 | # set up all variable and property definitions 2554 | 2555 | unset (COTIRE_C_COMPILER_VERSION CACHE) 2556 | unset (COTIRE_CXX_COMPILER_VERSION CACHE) 2557 | 2558 | if (NOT DEFINED COTIRE_DEBUG_INIT) 2559 | if (DEFINED COTIRE_DEBUG) 2560 | set (COTIRE_DEBUG_INIT ${COTIRE_DEBUG}) 2561 | else() 2562 | set (COTIRE_DEBUG_INIT FALSE) 2563 | endif() 2564 | endif() 2565 | option (COTIRE_DEBUG "Enable cotire debugging output?" ${COTIRE_DEBUG_INIT}) 2566 | 2567 | if (NOT DEFINED COTIRE_VERBOSE_INIT) 2568 | if (DEFINED COTIRE_VERBOSE) 2569 | set (COTIRE_VERBOSE_INIT ${COTIRE_VERBOSE}) 2570 | else() 2571 | set (COTIRE_VERBOSE_INIT FALSE) 2572 | endif() 2573 | endif() 2574 | option (COTIRE_VERBOSE "Enable cotire verbose output?" ${COTIRE_VERBOSE_INIT}) 2575 | 2576 | set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS "inc;inl;ipp" CACHE STRING 2577 | "Ignore headers with the listed file extensions from the generated prefix header.") 2578 | 2579 | set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH "" CACHE STRING 2580 | "Ignore headers from these directories when generating the prefix header.") 2581 | 2582 | set (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS "m;mm" CACHE STRING 2583 | "Ignore sources with the listed file extensions from the generated unity source.") 2584 | 2585 | set (COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES "3" CACHE STRING 2586 | "Minimum number of sources in target required to enable use of precompiled header.") 2587 | 2588 | set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES "" CACHE STRING 2589 | "Maximum number of source files to include in a single unity source file.") 2590 | 2591 | if (NOT COTIRE_PREFIX_HEADER_FILENAME_SUFFIX) 2592 | set (COTIRE_PREFIX_HEADER_FILENAME_SUFFIX "_prefix") 2593 | endif() 2594 | if (NOT COTIRE_UNITY_SOURCE_FILENAME_SUFFIX) 2595 | set (COTIRE_UNITY_SOURCE_FILENAME_SUFFIX "_unity") 2596 | endif() 2597 | if (NOT COTIRE_INTDIR) 2598 | set (COTIRE_INTDIR "cotire") 2599 | endif() 2600 | if (NOT COTIRE_PCH_ALL_TARGET_NAME) 2601 | set (COTIRE_PCH_ALL_TARGET_NAME "all_pch") 2602 | endif() 2603 | if (NOT COTIRE_UNITY_BUILD_ALL_TARGET_NAME) 2604 | set (COTIRE_UNITY_BUILD_ALL_TARGET_NAME "all_unity") 2605 | endif() 2606 | if (NOT COTIRE_CLEAN_ALL_TARGET_NAME) 2607 | set (COTIRE_CLEAN_ALL_TARGET_NAME "clean_cotire") 2608 | endif() 2609 | if (NOT COTIRE_CLEAN_TARGET_SUFFIX) 2610 | set (COTIRE_CLEAN_TARGET_SUFFIX "_clean_cotire") 2611 | endif() 2612 | if (NOT COTIRE_PCH_TARGET_SUFFIX) 2613 | set (COTIRE_PCH_TARGET_SUFFIX "_pch") 2614 | endif() 2615 | if (NOT COTIRE_UNITY_BUILD_TARGET_SUFFIX) 2616 | set (COTIRE_UNITY_BUILD_TARGET_SUFFIX "_unity") 2617 | endif() 2618 | if (NOT DEFINED COTIRE_TARGETS_FOLDER) 2619 | set (COTIRE_TARGETS_FOLDER "cotire") 2620 | endif() 2621 | if (NOT DEFINED COTIRE_UNITY_OUTPUT_DIRECTORY) 2622 | if ("${CMAKE_GENERATOR}" MATCHES "Ninja") 2623 | # generated Ninja build files do not work if the unity target produces the same output file as the cotired target 2624 | set (COTIRE_UNITY_OUTPUT_DIRECTORY "unity") 2625 | else() 2626 | set (COTIRE_UNITY_OUTPUT_DIRECTORY "") 2627 | endif() 2628 | endif() 2629 | 2630 | # define cotire cache variables 2631 | 2632 | define_property( 2633 | CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH" 2634 | BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." 2635 | FULL_DOCS 2636 | "The variable can be set to a semicolon separated list of include directories." 2637 | "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." 2638 | "If not defined, defaults to empty list." 2639 | ) 2640 | 2641 | define_property( 2642 | CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS" 2643 | BRIEF_DOCS "Ignore includes with the listed file extensions from the generated prefix header." 2644 | FULL_DOCS 2645 | "The variable can be set to a semicolon separated list of file extensions." 2646 | "If a header file extension matches one in the list, it will be excluded from the generated prefix header." 2647 | "Includes with an extension in CMAKE__SOURCE_FILE_EXTENSIONS are always ignored." 2648 | "If not defined, defaults to inc;inl;ipp." 2649 | ) 2650 | 2651 | define_property( 2652 | CACHED_VARIABLE PROPERTY "COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS" 2653 | BRIEF_DOCS "Exclude sources with the listed file extensions from the generated unity source." 2654 | FULL_DOCS 2655 | "The variable can be set to a semicolon separated list of file extensions." 2656 | "If a source file extension matches one in the list, it will be excluded from the generated unity source file." 2657 | "Source files with an extension in CMAKE__IGNORE_EXTENSIONS are always excluded." 2658 | "If not defined, defaults to m;mm." 2659 | ) 2660 | 2661 | define_property( 2662 | CACHED_VARIABLE PROPERTY "COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES" 2663 | BRIEF_DOCS "Minimum number of sources in target required to enable use of precompiled header." 2664 | FULL_DOCS 2665 | "The variable can be set to an integer > 0." 2666 | "If a target contains less than that number of source files, cotire will not enable the use of the precompiled header for the target." 2667 | "If not defined, defaults to 3." 2668 | ) 2669 | 2670 | define_property( 2671 | CACHED_VARIABLE PROPERTY "COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES" 2672 | BRIEF_DOCS "Maximum number of source files to include in a single unity source file." 2673 | FULL_DOCS 2674 | "This may be set to an integer > 0." 2675 | "If a target contains more than that number of source files, cotire will create multiple unity source files for it." 2676 | "If not set, cotire will only create a single unity source file." 2677 | "Is use to initialize the target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." 2678 | "Defaults to empty." 2679 | ) 2680 | 2681 | # define cotire directory properties 2682 | 2683 | define_property( 2684 | DIRECTORY PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" 2685 | BRIEF_DOCS "Modify build command of cotired targets added in this directory to make use of the generated precompiled header." 2686 | FULL_DOCS 2687 | "See target property COTIRE_ENABLE_PRECOMPILED_HEADER." 2688 | ) 2689 | 2690 | define_property( 2691 | DIRECTORY PROPERTY "COTIRE_ADD_UNITY_BUILD" 2692 | BRIEF_DOCS "Add a new target that performs a unity build for cotired targets added in this directory." 2693 | FULL_DOCS 2694 | "See target property COTIRE_ADD_UNITY_BUILD." 2695 | ) 2696 | 2697 | define_property( 2698 | DIRECTORY PROPERTY "COTIRE_ADD_CLEAN" 2699 | BRIEF_DOCS "Add a new target that cleans all cotire generated files for cotired targets added in this directory." 2700 | FULL_DOCS 2701 | "See target property COTIRE_ADD_CLEAN." 2702 | ) 2703 | 2704 | define_property( 2705 | DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" 2706 | BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." 2707 | FULL_DOCS 2708 | "See target property COTIRE_PREFIX_HEADER_IGNORE_PATH." 2709 | ) 2710 | 2711 | define_property( 2712 | DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" 2713 | BRIEF_DOCS "Honor headers from these directories when generating the prefix header." 2714 | FULL_DOCS 2715 | "See target property COTIRE_PREFIX_HEADER_INCLUDE_PATH." 2716 | ) 2717 | 2718 | define_property( 2719 | DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" 2720 | BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each source file." 2721 | FULL_DOCS 2722 | "See target property COTIRE_UNITY_SOURCE_PRE_UNDEFS." 2723 | ) 2724 | 2725 | define_property( 2726 | DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" 2727 | BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each source file." 2728 | FULL_DOCS 2729 | "See target property COTIRE_UNITY_SOURCE_POST_UNDEFS." 2730 | ) 2731 | 2732 | define_property( 2733 | DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" 2734 | BRIEF_DOCS "Maximum number of source files to include in a single unity source file." 2735 | FULL_DOCS 2736 | "See target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." 2737 | ) 2738 | 2739 | # define cotire target properties 2740 | 2741 | define_property( 2742 | TARGET PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" INHERITED 2743 | BRIEF_DOCS "Modify this target's build command to make use of the generated precompiled header." 2744 | FULL_DOCS 2745 | "If this property is set to TRUE, cotire will modify the build command to make use of the generated precompiled header." 2746 | "Irrespective of the value of this property, cotire will setup custom commands to generate the unity source and prefix header for the target." 2747 | "For makefile based generators cotire will also set up a custom target to manually invoke the generation of the precompiled header." 2748 | "The target name will be set to this target's name with the suffix _pch appended." 2749 | "Inherited from directory." 2750 | "Defaults to TRUE." 2751 | ) 2752 | 2753 | define_property( 2754 | TARGET PROPERTY "COTIRE_ADD_UNITY_BUILD" INHERITED 2755 | BRIEF_DOCS "Add a new target that performs a unity build for this target." 2756 | FULL_DOCS 2757 | "If this property is set to TRUE, cotire creates a new target of the same type that uses the generated unity source file instead of the target sources." 2758 | "Most of the relevant target properties will be copied from this target to the new unity build target." 2759 | "Target dependencies and linked libraries have to be manually set up for the new unity build target." 2760 | "The unity target name will be set to this target's name with the suffix _unity appended." 2761 | "Inherited from directory." 2762 | "Defaults to TRUE." 2763 | ) 2764 | 2765 | define_property( 2766 | TARGET PROPERTY "COTIRE_ADD_CLEAN" INHERITED 2767 | BRIEF_DOCS "Add a new target that cleans all cotire generated files for this target." 2768 | FULL_DOCS 2769 | "If this property is set to TRUE, cotire creates a new target that clean all files (unity source, prefix header, precompiled header)." 2770 | "The clean target name will be set to this target's name with the suffix _clean_cotire appended." 2771 | "Inherited from directory." 2772 | "Defaults to FALSE." 2773 | ) 2774 | 2775 | define_property( 2776 | TARGET PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" INHERITED 2777 | BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." 2778 | FULL_DOCS 2779 | "The property can be set to a list of directories." 2780 | "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." 2781 | "Inherited from directory." 2782 | "If not set, this property is initialized to \${CMAKE_SOURCE_DIR};\${CMAKE_BINARY_DIR}." 2783 | ) 2784 | 2785 | define_property( 2786 | TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" INHERITED 2787 | BRIEF_DOCS "Honor headers from these directories when generating the prefix header." 2788 | FULL_DOCS 2789 | "The property can be set to a list of directories." 2790 | "If a header file is found in one of these directories or sub-directories, it will be included in the generated prefix header." 2791 | "If a header file is both selected by COTIRE_PREFIX_HEADER_IGNORE_PATH and COTIRE_PREFIX_HEADER_INCLUDE_PATH," 2792 | "the option which yields the closer relative path match wins." 2793 | "Inherited from directory." 2794 | "If not set, this property is initialized to the empty list." 2795 | ) 2796 | 2797 | define_property( 2798 | TARGET PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" INHERITED 2799 | BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each target source file." 2800 | FULL_DOCS 2801 | "This may be set to a semicolon-separated list of preprocessor symbols." 2802 | "cotire will add corresponding #undef directives to the generated unit source file before each target source file." 2803 | "Inherited from directory." 2804 | "Defaults to empty string." 2805 | ) 2806 | 2807 | define_property( 2808 | TARGET PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" INHERITED 2809 | BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each target source file." 2810 | FULL_DOCS 2811 | "This may be set to a semicolon-separated list of preprocessor symbols." 2812 | "cotire will add corresponding #undef directives to the generated unit source file after each target source file." 2813 | "Inherited from directory." 2814 | "Defaults to empty string." 2815 | ) 2816 | 2817 | define_property( 2818 | TARGET PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" INHERITED 2819 | BRIEF_DOCS "Maximum number of source files to include in a single unity source file." 2820 | FULL_DOCS 2821 | "This may be set to an integer > 0." 2822 | "If a target contains more than that number of source files, cotire will create multiple unity build files for it." 2823 | "If not set, cotire will only create a single unity source file." 2824 | "Inherited from directory." 2825 | "Defaults to empty." 2826 | ) 2827 | 2828 | define_property( 2829 | TARGET PROPERTY "COTIRE__UNITY_SOURCE_INIT" 2830 | BRIEF_DOCS "User provided unity source file to be used instead of the automatically generated one." 2831 | FULL_DOCS 2832 | "If set, cotire will only add the given file(s) to the generated unity source file." 2833 | "If not set, cotire will add all the target source files to the generated unity source file." 2834 | "The property can be set to a user provided unity source file." 2835 | "Defaults to empty." 2836 | ) 2837 | 2838 | define_property( 2839 | TARGET PROPERTY "COTIRE__PREFIX_HEADER_INIT" 2840 | BRIEF_DOCS "User provided prefix header file to be used instead of the automatically generated one." 2841 | FULL_DOCS 2842 | "If set, cotire will add the given header file(s) to the generated prefix header file." 2843 | "If not set, cotire will generate a prefix header by tracking the header files included by the unity source file." 2844 | "The property can be set to a user provided prefix header file (e.g., stdafx.h)." 2845 | "Defaults to empty." 2846 | ) 2847 | 2848 | define_property( 2849 | TARGET PROPERTY "COTIRE__UNITY_SOURCE" 2850 | BRIEF_DOCS "Read-only property. The generated unity source file(s)." 2851 | FULL_DOCS 2852 | "cotire sets this property to the path of the generated single computation unit source file for the target." 2853 | "Defaults to empty string." 2854 | ) 2855 | 2856 | define_property( 2857 | TARGET PROPERTY "COTIRE__PREFIX_HEADER" 2858 | BRIEF_DOCS "Read-only property. The generated prefix header file." 2859 | FULL_DOCS 2860 | "cotire sets this property to the full path of the generated language prefix header for the target." 2861 | "Defaults to empty string." 2862 | ) 2863 | 2864 | define_property( 2865 | TARGET PROPERTY "COTIRE__PRECOMPILED_HEADER" 2866 | BRIEF_DOCS "Read-only property. The generated precompiled header file." 2867 | FULL_DOCS 2868 | "cotire sets this property to the full path of the generated language precompiled header binary for the target." 2869 | "Defaults to empty string." 2870 | ) 2871 | 2872 | define_property( 2873 | TARGET PROPERTY "COTIRE_UNITY_TARGET_NAME" 2874 | BRIEF_DOCS "The name of the generated unity build target corresponding to this target." 2875 | FULL_DOCS 2876 | "This property can be set to the desired name of the unity target that will be created by cotire." 2877 | "If not set, the unity target name will be set to this target's name with the suffix _unity appended." 2878 | "After this target has been processed by cotire, the property is set to the actual name of the generated unity target." 2879 | "Defaults to empty string." 2880 | ) 2881 | 2882 | # define cotire source properties 2883 | 2884 | define_property( 2885 | SOURCE PROPERTY "COTIRE_EXCLUDED" 2886 | BRIEF_DOCS "Do not modify source file's build command." 2887 | FULL_DOCS 2888 | "If this property is set to TRUE, the source file's build command will not be modified to make use of the precompiled header." 2889 | "The source file will also be excluded from the generated unity source file." 2890 | "Source files that have their COMPILE_FLAGS property set will be excluded by default." 2891 | "Defaults to FALSE." 2892 | ) 2893 | 2894 | define_property( 2895 | SOURCE PROPERTY "COTIRE_DEPENDENCY" 2896 | BRIEF_DOCS "Add this source file to dependencies of the automatically generated prefix header file." 2897 | FULL_DOCS 2898 | "If this property is set to TRUE, the source file is added to dependencies of the generated prefix header file." 2899 | "If the file is modified, cotire will re-generate the prefix header source upon build." 2900 | "Defaults to FALSE." 2901 | ) 2902 | 2903 | define_property( 2904 | SOURCE PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" 2905 | BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of this source file." 2906 | FULL_DOCS 2907 | "This may be set to a semicolon-separated list of preprocessor symbols." 2908 | "cotire will add corresponding #undef directives to the generated unit source file before this file is included." 2909 | "Defaults to empty string." 2910 | ) 2911 | 2912 | define_property( 2913 | SOURCE PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" 2914 | BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of this source file." 2915 | FULL_DOCS 2916 | "This may be set to a semicolon-separated list of preprocessor symbols." 2917 | "cotire will add corresponding #undef directives to the generated unit source file after this file is included." 2918 | "Defaults to empty string." 2919 | ) 2920 | 2921 | define_property( 2922 | SOURCE PROPERTY "COTIRE_START_NEW_UNITY_SOURCE" 2923 | BRIEF_DOCS "Start a new unity source file which includes this source file as the first one." 2924 | FULL_DOCS 2925 | "If this property is set to TRUE, cotire will complete the current unity file and start a new one." 2926 | "The new unity source file will include this source file as the first one." 2927 | "This property essentially works as a separator for unity source files." 2928 | "Defaults to FALSE." 2929 | ) 2930 | 2931 | define_property( 2932 | SOURCE PROPERTY "COTIRE_TARGET" 2933 | BRIEF_DOCS "Read-only property. Mark this source file as cotired for the given target." 2934 | FULL_DOCS 2935 | "cotire sets this property to the name of target, that the source file's build command has been altered for." 2936 | "Defaults to empty string." 2937 | ) 2938 | 2939 | endif() 2940 | --------------------------------------------------------------------------------